正在加载内容…
拓展单向循环链表
909 字
5 分钟
拓展单向循环链表
本文讲解单向循环链表的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;}————————完结撒花!!!——————希望对你有所帮助!
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
相关文章 智能推荐
1
附录对于头结点单向链表的优化方法
数据结构 本文介绍无头结点单向链表的优化:用栈上dummy节点临时充当头结点,遍历或增删结束后还原header指针,使结构统一,并给出初始化、插入、删除及销毁实现。
2
链表的实现与介绍
数据结构 本文介绍链表的结构与C语言实现,针对顺序表内存碎片问题引出链式存储。讲解节点数据域与地址域,给出初始化、按位插入、按值删除及遍历等函数代码。讨论限于无操作系统内存分配场景。
3
链表最终章双向链表及其应用
数据结构 本文讲解带头节点的双向循环链表,用静态函数封装插入与删除,并实现初始化、头尾插、查找删除、展示和销毁,说明双向结构虽增加代码复杂度但操作更便捷。
4
顺序表的实现与介绍及部分工程思想的思考
数据结构 本文介绍堆空间动态顺序表的实现,含表头data、pos、capacity,用malloc和free管理,扩容两倍并搬移数据,讨论插入删除的搬移逻辑与防泄露的工程思想,并以测试验证。
5
数据结构之树及树的存储
数据结构 本文介绍树的基本概念与存储表示法:双亲表示法找父节点快、找孩子需遍历;孩子表示法相反;孩子兄弟表示法用二叉链表存左孩子右兄弟,便于查孩子和兄弟。
随机文章 随机推荐
评论区
正在加载 GitHub 评论…