· 拓展单向循环链表

拓展单向循环链表

909 字
5 分钟
拓展单向循环链表
StarryLin/Geek · 摘要

本文讲解单向循环链表的C语言实现,先分析带头结点与尾指针的结构设计,再给出初始化、头插、尾插、删除、遍历和销毁函数。边界是仅讨论实现思路,未展开约瑟夫环测试。

—————————————本文旨在讨论计算机知识欢迎指正——————————————— 书接上回:我们已经了解了链表如何编写与前置节点和头指针两种表示方式,下面,我们来了解进阶写法———如何实现单向循环链表。 下面,我们来梳理一下循环链表的实现方式: 这是朴素的链表实现形式: 这是我们理想中的循环链表实现形式: 这就是我们大体的思路,然后,我们将要实现它:

  • 首先,我们第一个难点就是———如何来构造链表的结构体;因为我们需要循环,那么我们必须要知道尾巴在哪里,所以结构体里必须有tail这一容器,然后我们还要考虑如果每次我们都用一个迭代器去迭代遍历找尾巴tail的话,效率会非常低,我们需要一个更好的表示形式
// 定义节点结构
typedef int Element;
typedef struct _loop_node {
Element val;//数据域
struct _loop_node *next;//地址域,指向下一个
} LoopNode;
// 定义单向循环链表的头结构
typedef struct {
LoopNode header;//头结点
LoopNode *tail;//尾巴的容器,相当于一个移动指针迭代器;
int num;//计数器,用来遏制循环次数,防止无限循环
} LinkLoopList;

于是,笔者想到了两种结构,分别是头插法和尾插法: 1、头插法:

int insertLinkLoopHeader(LinkLoopList* link_loop, Element value) {
// 1. 先有新节点
LoopNode *node = malloc(sizeof(LoopNode));
if (node == NULL) {
return -1;
}
node->val = value;//新节点赋值
node->next = link_loop->header.next;//插到头结点的下一个元素的前面;
link_loop->header.next = node;//更新头结点在新节点的前面
// 2. 判断尾指针是否需要更新
if (link_loop->tail== &link_loop->header) {
link_loop->tail = node;
}
++link_loop->num;
return 0;
}

2、尾插法:

int insertLinkLoopTail(LinkLoopList* link_loop, Element value) {
// 1. 先有新节点
LoopNode *node = malloc(sizeof(LoopNode));
if (node == NULL) {
return -1;
}
node->val = value;
node->next = link_loop->tail->next;//尾部插入
link_loop->tail->next = node;//与原尾部节点衔接
link_loop->tail = node;//更新尾部
++link_loop->num;//更新数量
return 0;
}

然后我们从头考虑初始化和声明结构体:

// 定义节点结构
typedef int Element;
typedef struct _loop_node {
Element val;
struct _loop_node *next;
} LoopNode;
// 定义单向循环链表的头结构
typedef struct {
LoopNode header;
LoopNode *rear;
int num;
} LinkLoopList;

这是初始化:

void initLinkLoopList(LinkLoopList* link_loop) {
link_loop->header.next = link_loop->tail = &link_loop->header;//头指针和尾指针同时指向头结点
link_loop->num = 0;
}

删除:

int deleteLinkLoopList(LinkLoopList* link_loop, Element value) {
LoopNode *node = &link_loop->header;
while (node->next != &link_loop->header && node->next->val != value) {
node = node->next;
}
if (node->next->val == value) {
LoopNode *tmp = node->next;
node->next = tmp->next;
free(tmp);//释放空间,删除操作
--link_loop->num;
} else {
printf("No %d element!\n", value);
}
return 0;
}

呈现操作:

void showLinkLoopList(const LinkLoopList* link_loop) {
LoopNode *node = link_loop->header.next;
while (node != &link_loop->header) {
printf("\t%d", node->val);
node = node->next;
}
printf("\n");
}

销毁函数:

void destroyLinkLoopList(LinkLoopList* link_loop) {
LoopNode *node = link_loop->header.next;
while (node != &link_loop->header) {
LoopNode *tmp = node;
node = node->next;
free(tmp);
--link_loop->num;
}
printf("Table %d element!\n", link_loop->num);
}

最后,是测试函数:

void test01() {
LinkLoopList table;
initLinkLoopList(&table);
for (int i = 0; i < 10; ++i) {
insertLinkLoopRear(&table, i + 100);
}
showLinkLoopList(&table);
printf("======================\n");
deleteLinkLoopList(&table, 106);
showLinkLoopList(&table);
destroyLinkLoopList(&table);
}
/* 以下是几组约瑟夫环的测试答案,包括每个被杀者的顺序编号和最后的幸存者:
* 当n=5,k=2时,被杀者的顺序编号为2, 4, 1, 5,最后的幸存者是3。
* 当n=10,k=3时,被杀者的顺序编号为3, 6, 9, 2, 7, 1, 8, 5, 10,最后的幸存者是4。
* 当n=7,k=2时,被杀者的顺序编号为2, 4, 6, 1, 5, 3,最后的幸存者是7。
* 当n=10,k=17时,最后的幸存者是3
*/
void test_Joseph() {
}
int main() {
test01();
return 0;
}

————————完结撒花!!!——————希望对你有所帮助!

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

拓展单向循环链表
https://mjzy.tech/posts/knowledge/拓展单向循环链表/
作者
JIA
发布于
2026-06-21
许可协议
CC BY-NC-SA 4.0

评论区

正在加载 GitHub 评论…

Tips · 随手记
仅自己可见 · 每篇文章单独保存

文章目录