这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战
循环链表
线性链表存在的问题: 线性链表知道了某个链结点的指针,可以比较方便地访问其后面的结点,但无法根据该指针访问到前面的结点
循环链表: 指链表中最后那个链结点的指针域不再存放NULL,而是指向链表的第1个链结点,整个链表形成一个环。
解决了想从表中任意一个链结点出发都可以访问到其他链结点的问题。
循环链表的遍历
循环链表可以周而复始地访问链表中所有结点。
遍历整个链表:
- 设一个活动指针p,令其初始时指向头结点(p=list)
- 反复指向赋值语句 p = p->link直到p等于list(p回到头结点)
循环链表的操作与线性链表的操作基本相同,不同部分如下:
- 循环条件是不是判断
p || p->link 是否为 NULL,而是判断它们是否为链表最前面那个链结点的指针
循环链表相关例题
例2.5 在一个带有头结点的循环链表中查找一个数据信息为item的链结点
若查找成功,返回该链结点的指针,否则返回NULL
算法:
LinkList SEARCHKEY(LinkList list, ElemType item)
{
LinkList p;
p = list->link; // 从头结点的下一个结点开始查找
while (p != list) {
if (p->data == item)
return p; // 查找成功
p = p->link;
}
return NULL; // 查找失败
}
例2.6 约瑟夫(Josephu)问题 P47
比较简单的方式是利用一个具有n个链结点、不带头结点的循环链表。整个算法可分为3个部分:
- 建立一个具有n个链结点且无头结点的循环链表
- 确定第1个报数点的位置
- 不断地从链表中删除一个链结点,直至链表中只剩有一个链结点
算法:
void JOSEPHUS(int n, int m, int k)
{
LinkList p, r, list = NULL;
for (i = 1; i <= n; i++) {
p = (LinkList)malloc(sizeof(LNode)); // 申请一个新的链结点
p->data = i; // 存放第i个结点的编号
if (list == NULL)
list = p;
else
r->link = p;
r = p;
}
p->link = list; // 至此,建立一个循环链表
p = list;
for (i = 1; i < k; i++) {
r = p;
p = p->link;
} // 此时p指向第1个出发结点
while(p->link != p) {
for (i = 1; i < m; i++) {
r = p; // 不多余,当k不等于1,但m=1时,如果没有这条语句,此时删除动作将无法进行
p = p->link;
} // p指向第m个结点,r指向第m-1个结点
r->link = p->link; // 删除第m个结点
printf("%4d", p->data); // 输出一个结点编号
free(p); // 释放被删除结点的空间
p = r->link; // p指向新的出发结点
}
printf("\n最后被删除的结点是%4d\n", p->data); // 输出最后那个结点的编号
}