目录
循环链表,双向链表
一、循环链表
-
是一种头尾相接的链表(即表中最后一个结点的指针域指向头结点,整个链表形成一个环)。
-
当循环单链表为空表时,头结点的指针域指向头结点。
-
优点
-
从表中任一结点出发均可找到表中其他结点。
-
-
由于循环链表中没有NULL指针,故涉及遍历操作时,其终止条件不再像非循环链表那样 判断 p 或 p->next是否为空 ,而是 判断它们是否等于头指针(p != L 或 p->next != L)。
-
表的操作通常时在表的 首尾位置 上进行。
带尾指针循环链表的合并
将Tb合并在Ta之后
思路:
-
p存表头结点:p = Ta->next;
-
Tb表头连接到Ta表尾:Ta->next = Tb->next->next;
-
释放Tb表头结点:delete Tb->next;
-
修改指针:Tb->next = p;
算法描述:
LinkList Connect(LinkList Ta, LinkList Tb) { //假设Ta、Tb都是非空的单循环链表
p = Ta->next; //p存Ta表头结点
Ta->next = Tb->next->next; //Tb表头连接Ta表尾
delete Tb->next; //释放Tb表头结点
Tb->next = p; //修改指针
return Tb;
}
-
时间复杂度 O(1)。
二、双向链表
单链表在查找某个结点的前驱结点时令人头痛,因为它每个结点只有一个指向后继结点的指针(next)。双向链表就不同了,双向链表不仅和单链表一样有一个指向后继结点的指针(next),还比单链表多了一个指向前驱结点的指针(prior)。
双向链表的定义
typedef struct DuLnode {
ElemType data;
struct DuLnode *prior, *next;
} DuLnode, *DuLinkList;
双向链表为空表时,prior和next都指向NULL。
-
of course,当尾结点的next域指向头结点,头结点的prior域指向尾结点时,就成了 双向循环链表!
双向循环链表为空表时,prior和next都指向头结点。
-
双向链表结构的 对称性 (设指针p指向某一结点):
p->prior->next = p = p->next->prior;
在双向链表中有些操作(如ListLength、GetElem等),只需要操作一个方向上的指针就可以完成,和单链表一样。但在插入、删除时,则需要同时修改两个方向上的指针,两者的操作的时间复杂度均为O(n)。
双向链表的插入
思路:
-
s->next = p->prior;
-
p->prior->next = s;
-
s->next = p;
-
p->prior = s;
第4步一定要在第1、2步之后,其他顺序可以交换!!
算法描述:
Status ListInsert_DuL(DuLinkList &L, int i, ElemType e) {
//在带头结点的双向链表L中第i个位置之前插入元素e
if(!(p = GetElemP_DuL(L,i))) return ERROR;
s = new DuLNode;
s->data = e;
s->prior = p->prior;
p->prior->next = s;
s->next = p;
p->prior = s;
return OK;
}
就插入操作而言 时间复杂度为 O(1),但若是要加上查找,时间复杂度就为 O(n)。
双向链表的删除
只需要两步,把要删除的结点的前驱和后继连起来,再把要删除的结点释放掉就可以了。
思路:
若指针p指向要删除的结点
-
p->prior->next = p->next;
p->next->prior = p->prior;
-
delete p;
算法描述:
Status ListDelete_DuL(DuLink &L, int i, ElemType &e) {
//删除带头结点的双向循环链表L的第i个元素,并用e返回(也可以不返回)。
if(!(p = GetElemP_DuL(L,i))) return ERROR;
e = p->data;
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
return OK;
}
就删除操作而言 时间复杂度为 O(1),但若是要加上查找,时间复杂度就为 O(n)。
单、双、循环链表时间效率比较
查找表头结点(首元结点) | 查找表尾结点 | 查找结点*p的前驱结点 | |
---|---|---|---|
带头结点的单链表L | L->next 时间复杂度O(1) | 从L->next依次向后遍历 时间复杂度O(n) | 通过p->next无法找到其前驱 |
带头结点仅设头指针L的循环单链表 | L->next 时间复杂度O(1) | 从L->next依次向后遍历 时间复杂度O(n) | 通过p->next可以找到其前驱 时间复杂度O(n) |
带头结点仅设尾指针R的循环单链表 | R->next->next 时间复杂度O(1) | R 时间复杂度O(1) | 通过p->next可以找到其前驱 时间复杂度O(n) |
带头结点的双向循环链表L | L->next 时间复杂度O(1) | L->prior 时间复杂度O(1) | p->prior 时间复杂度O(1) |