![数据结构(C语言实现)](https://wfqqreader-1252317822.image.myqcloud.com/cover/699/43806699/b_43806699.jpg)
2.3.5 双向链表
在前面讨论过的单链表和循环链表中,每个结点的指针域只有一个,用来存放后继结点的指针,而没有关于前驱结点的信息。因此,从某个结点出发,只能顺着指针往后查找其他结点。若要查找结点的前驱,这需要从表头结点开始,顺着指针寻找,显然,使用单链表处理不够方便。同样,从单链表中删除一个结点也会遇到类似的问题。为了克服单链表的这种缺点,可以使用双向链表。
1.双向链表的存储结构
双向链表中,每个结点有两个指针域:一个指向直接前驱结点,另一个指向直接后继结点。双向链表的结点结构如图2.28所示。
在双向链表中,每个结点包括3个域:data域、prior域和next域。其中,data域为数据域,存放数据元素;prior域为前驱结点指针域,指向直接前驱结点;next域为后继结点指针域,指向直接后继结点。
双向链表也可分为带头结点和不带头结点两种,带头结点使某些操作更加方便。另外,双向链表也有循环结构,称为双向循环链表。带头结点的双向循环链表如图2.29所示。
![](https://epubservercos.yuewen.com/2EFA35/23083815801896206/epubprivate/OEBPS/Images/50_03.jpg?sign=1738883908-Sel9UTsk1VVs1SAMslVsKwwGeXDLRnaV-0-7eb59fd11b010c533ac8e13f19ab8c81)
图2.28 双向链表的结点结构
![](https://epubservercos.yuewen.com/2EFA35/23083815801896206/epubprivate/OEBPS/Images/50_04.jpg?sign=1738883908-AgbWSSU87QKLdXjrXpE3zYK1HREWahh0-0-6f010e93404a8f67f4db3cce36376eaa)
图2.29 带头结点的双向循环链表
双向循环链表为空的情况如图2.30所示,判断带头结点的双循环链表为空的条件是head->prior==head或head->next==head。
在双向链表中,每个结点既有前驱结点的指针域又有后继结点的指针域,因此查找结点非常方便。如果p是指向链表中某个结点的指针,则有p=p->prior->next=p->next->prior。
![](https://epubservercos.yuewen.com/2EFA35/23083815801896206/epubprivate/OEBPS/Images/50_05.jpg?sign=1738883908-eIxx4IObgMHg1OAp2pMOjEJWtoisZMI6-0-6ec8331707d7afa496c8e92434480121)
图2.30 带头结点的双向循环链表为空时的情况
双向链表的结点类型描述如下:
![](https://epubservercos.yuewen.com/2EFA35/23083815801896206/epubprivate/OEBPS/Images/50_06.jpg?sign=1738883908-LRnEmPYqYfWnIxazUvB4Bkbyr65m3m7h-0-ace4a527273adcb0cec136cd71e90f4d)
2.双向链表的插入操作和删除操作
对于双向链表的有些操作,如求链表的长度、查找链表的第i个结点等,与单链表中的算法实现基本没什么差异。但是,对于双向循环链表的插入操作和删除操作,因为涉及的是前驱结点指针和后继结点指针,所以需要修改两个方向的指针。
(1)插入操作
插入操作就是要在双向循环链表的第i个位置插入一个元素值为e的结点。插入成功返回1,否则返回0。
【算法思想】首先找到第i个结点,用p指向该结点。再申请一个新结点,由s指向该结点,将e放入到数据域。然后开始修改p和s指向的结点的指针域:修改s的prior域,使其指向p的直接前驱结点,即s->prior=p->prior;将p的直接前驱结点的next域,使其指向s指向的结点,即p->prior->next=s;修改s的next域,使其指向p指向的结点,即s->next=p;修改p的prior域,使其指向s指向的结点,即p->prior=s。插入操作指针修改情况如图2.31所示。
![](https://epubservercos.yuewen.com/2EFA35/23083815801896206/epubprivate/OEBPS/Images/51_01.jpg?sign=1738883908-CBTJkVj7LJ5ErwiCUKlVOtCdqYt6NEat-0-88be39679d7dbbd1a3faa57c5eb39346)
图2.31 双向循环链表的插入操作过程
插入操作算法实现如下。
![](https://epubservercos.yuewen.com/2EFA35/23083815801896206/epubprivate/OEBPS/Images/51_02.jpg?sign=1738883908-nG2MpSjQaUWMacmIpvD6pJv4TVRiFS6O-0-1b210066f964965a6bbe03acb81674a5)
(2)删除操作
删除操作就是将带头结点的双向循环链表中的第i个结点删除。删除成功返回1,否则返回0。
【算法思想】首先找到第i个结点,用p指向该结点。然后开始修改p指向的结点的直接前驱结点和直接后继结点的指针域,从而将p与链表断开。将p指向的结点与链表断开需要两步:
①修改p的直接前驱结点的next域,使其指向p的直接后继结点,即p->prior->next=p->next。
②修改p的直接后继结点的prior域,使其指向p的直接前驱结点,即p->next->prior=p->prior。
删除操作指针修改情况如图2.32所示。
![](https://epubservercos.yuewen.com/2EFA35/23083815801896206/epubprivate/OEBPS/Images/52_01.jpg?sign=1738883908-c6ao5wKjL4Ve77IjjWpMWHzd1wo9yxZV-0-11264e3a3fa89f4c295437fb3f663e0e)
图2.32 双向循环链表的删除操作过程
删除操作算法实现如下。
![](https://epubservercos.yuewen.com/2EFA35/23083815801896206/epubprivate/OEBPS/Images/52_02.jpg?sign=1738883908-ydmGe2KFe1hP1o6XmMV0a26HuWBOsTMk-0-daccadb5286e4f853740655508e8c13a)
插入操作和删除操作的时间耗费主要在查找结点上,两者的时间复杂度都为O(n)。
注意:双向链表的插入操作和删除操作需要修改结点的prior域和next域,因此要注意修改结点指针域的顺序。