我们这里接着上一篇单链表继续往下深入学习循环链表、双向链表。
链表
🎈3.循环链表
🔭3.1循环链表的概念
循环链表
是一种头尾相接的链表。它的特点是由链表的尾结点的指针域不为空,而是指向头结点,整个链表形成一个环。由此,从表中任何一个结点出发均可找到链表的其他结点。在单链表中,将终端结点的指针域NULL改为指向表头结点的或开始结点,就得到了单链形式的循环链表,并简称为单循环链表。与单链表一样,为了使空表和非空表处理一致,循环链表也可以设置一个头结点。空循环链表和非空循环链表如图所示:
🏆单循环链表的基本算法与非循环单链表的算法基本相同,只需对表尾的判断条件做出修改即可,例如,判断表尾结点的条件为:
p->next==head
.
🔭3.2循环链表的基本操作
🔎对于上述这个非空单循环链表:
✅当我们用头指针表示单循环链表:找a1的时间复杂度为:O(1),找an的时间复杂度为:O(n).
✅当我们用尾指针表示单循环链表:找a1的时间复杂度为:O(1)(R->next->next
),找an的时间复杂度为:O(1)(R
).
🏆因此,我们通常采用尾指针的单循环链表。
🔎3.2.1创建空表
void InitList(LNode *&rear)
{
rear = new LNode;//创建头结点
rear->next = rear;
}
🔎3.2.2插入操作
void InsertList(LNode *&rear, ElemType e)
{//在表尾插入一个结点
LNode* s = new LNode;
s->data = e;
s->next = rear->next;
rear->next = s;
rear = s;
}
🔎3.2.3删除操作
void Delete(LNode*& rear, ElemType& e)
{//删除链表的第一个结点
if (rear->next == rear)
return;//空表,无法删除元素
LNode* p = rear->next->next;//p指向第一个元素
e = p->data;
rear->next->next = p->next;
if (p == rear)
rear = p->next;
delete p;
}
🎈4.双向链表
双向链表是在单链表的每一个结点里再增加一个指向其直接前驱的指针域
prior
,这样形成的链表有两个方向不同的链,故称为双向链表。
🔭4.1类型定义
typedef int ElemType;
typedef struct DNode
{
ElemType data;//存放数据元素信息
DNode* next;//存放后继结点的地址信息
DNode* prior;//存放前驱结点的地址信息
}DNode;
🔭4.2头插法创建双向链表
void CreatList_h(DNode*& head, int n)
{
DNode* s;
head = new DNode;//创建头结点
head->next = head->prior = NULL;//前后指针置为NULL
int i = 0;
for (i = 0; i < n; i++)//循环创建数据结点
{
s = new DNode;
cin >> s->data;//读入数据
s->next = head->next;//连接s的两个指针
s->prior = head;
if (head->next != NULL)
head->next->prior = s;
head->next = s;
}
}
🔭4.3尾插法创建双向链表
void CreatList_t(DNode*& head, int n)
{
DNode* s, * rear;
head = new DNode;//创建头结点
head->next = head->prior = NULL;//前后指针置为NULL
rear = head;
int i = 0;
for (i = 0; i < n; i++)//循环创建数据结点
{
s = new DNode;//创建新的数据结点
cin >> s->data;//读入数据
s->prior = rear;//s插在rear后面
rear->next = s;
rear = s;
}
rear->next = NULL;
}
🔭4.4插入操作
void InsertDlist(DNode*& head, int i, ElemType e)
{
int j = 0;//计数器
DNode* p = head,*s;
while (p && j < i - 1)
{
p = p->next;
j++;
}
if (!p || j > i - 1)
return;
else//找到第i-1个结点
{
s = new DNode;
s->data = e;
s->next = p->next;//p结点后插入s结点
s->prior = p;
if (p->next)
p->next->prior = s;
}
}
🔭4.5删除操作
DeleteDList()
函数在双向链表中,结点的删除操作涉及前驱结点和后继结点两个指针域的变化。设在双向链表中删除指针p所指向结点的后继结点q,需要修改两个指针域,删除操作指针变化过程如图所示:
void DeleteDList(DNode*& head, int i, ElemType& e)
{
int j = 0;//计数器
DNode* p = head, * q;
while (p->next && j < i - 1)
{
p = p->next;
j++;
}
if (!(p->next) || j > i - 1)
return;
else
{
q = p->next;
e = q->data;
p->next = q->next;//删除q所指结点
if (q->next != NULL)
q->next->prior = p;
delete q;
}
}
好啦,关于循环链表和双向链表的知识点到这里就结束啦,后期会继续更新数据结构与算法的相关知识,欢迎大家持续关注、点赞和评论!❤️❤️❤️