【数据结构和算法】实现带头双向循环链表(最复杂的链表)(下)

简介: 【数据结构和算法】实现带头双向循环链表(最复杂的链表)(下)

7.删除指定pos结点

如图所示:

代码如下:

1. 
2. //删除指针
3. void ListEarse(DSLNode* pos) {
4.  assert(pos);
5.  DSLNode* cur = pos->prev;
6.  DSLNode* tail = pos->next;
7.  cur->next = tail;//两者相互指向,最后释放pos空间
8.  tail->prev = cur;
9.  free(pos);
10.   pos = NULL;
11. }

8.摧毁链表

两种方式,可以直接使用二级指针,也可以使用一级指针一个一个free和NULL

1. //摧毁链表
2. void ListDestory(DSLNode* ps) {
3.  //可以设计二级指针直接将ps地址滞空为NULL
4.  //这里还是使用一级指针
5.  assert(ps);
6.  DSLNode* cur = ps->next;
7.  while (cur != ps) {
8.    DSLNode* tail = cur->next;//这个地方就是一个精彩的地方
9.    free(cur);//使用临时变量tail得到cur的下一个地址,然后再free cur
10.     cur = tail;//tail这个时候就是cur 的下一个结点
11.   }
12.   free(ps);
13.   ps = NULL;
14. 
15. }
16. void ListDestory2(DSLNode** ps) {
17.   assert(ps);
18.   free(ps);//二级指针直接free
19.   *ps = NULL;
20. }

三、完整代码

1.DSLinkList.h

1. #define _CRT_SECURE_NO_WARNINGS
2. 
3. #include<stdio.h>
4. #include<stdlib.h>
5. #include<malloc.h>
6. #include<assert.h>
7. #include<stdbool.h>
8. 
9. typedef struct DSLDataType;
10. //定义双向链表的结构体
11. 
12. //双向链表每一个节点都有一个前驱节点和后继节点,可以根据节点获得其前后的节点
13. typedef struct DSListNode {
14.   struct DSListNode* prev;//前驱节点
15.   struct DSListNode* next;//后继节点
16.   int value;//数据域
17. }DSLNode;//重定义
18. 
19. //创建头节点,并将tail和head都指向第一个节点
20. struct DSList {
21.   struct DSListNode* tail;
22.   struct DSListNode* head;
23.   unsigned int len;//表示链表的长度
24. };
25. //初始化
26. DSLNode*ListInit();
27. //打印链表
28. void ListPrint(DSLNode* ps);
29. //尾部插入
30. void ListPushBack(DSLNode* ps, int data);
31. //头部插入
32. void ListPushFront(DSLNode* ps, int data);
33. //尾部删除
34. void ListPopBack(DSLNode* ps);
35. //头部删除
36. void ListPopFront(DSLNode* ps);
37. //判空
38. bool ListEmpty(DSLNode* ps);
39. //返回链表节点个数
40. int Listsize(DSLNode* ps);
41. //寻找指定元素,返回对应的节点
42. DSLNode* ListFind(DSLNode* ps, int data);
43. //在pos之前插入节点
44. void ListInsert(DSLNode* pos, int data);
45. //删除pos位置结点
46. void ListEarse(DSLNode* pos);
47. //摧毁链表
48. void ListDestory(DSLNode* ps);
49.

2.DSLinkList.c

1. #define _CRT_SECURE_NO_WARNINGS
2. 
3. #include"DSLinkList.h"
4. 
5. //对于函数的实现
6. //初始化
7. DSLNode* ListInit() {
8.  //初始化创建链表
9.  //创建新节点
10.   DSLNode* num = (DSLNode*)malloc(sizeof(DSLNode));
11.   //判断是否创建成功
12.   if (num == NULL) {
13.     perror("malloc fail\n");
14.     exit(-1);
15.   }
16.   num->prev = num;
17.   num->next = num;
18.   return num;
19. }
20. 
21. //打印链表
22. void ListPrint(DSLNode* ps) {
23.   assert(ps);//断言
24.   DSLNode* cur = ps->next;
25.   printf("phead->");
26.   while (cur != ps) {//双向链表,回到ps,就表示转了一圈
27.     printf("%d->", cur->value);
28.     cur = cur->next;
29.   }
30.   printf("NULL\n");
31. }
32. 
33. DSLNode* CreatNode(int data) {//创建新节点
34.   DSLNode* cur = (DSLNode*)malloc(sizeof(DSLNode));
35.   if (cur == NULL) {
36.     perror("malloc fail\n");
37.     exit(-1);
38.   }
39.   cur->next = cur->prev = NULL;
40.   cur->value = data;
41. }
42. //尾部插入
43. void ListPushBack(DSLNode* ps, int data) {
44.   assert(ps);//断言
45.   DSLNode* newnode = CreatNode(data);
46.   DSLNode* tail = ps->prev;//把头节点之前的那个地址给tail
47.   tail->next = newnode;//将ps之前的那个数值,实际上是这个双向链表的最后一个元素的位置,的next指针指向新节点
48.   newnode->prev = tail;//新节点前后为 tail和ps
49.   newnode->next = ps;
50.   ps->prev = newnode;//tail的两个指针域都指向完成,就只剩下ps的前驱指针
51. 
52. }
53. 
54. //头部插入
55. void ListPushFront(DSLNode* ps, int data) {
56.   assert(ps);
57.   //头部插入就是将ps之后的一个地址给临时变量tail
58.   DSLNode* tail = ps->next;
59.   DSLNode* newnode = CreatNode(data);//创建新节点
60.   ps->next->prev = newnode;
61.   newnode->next = ps->next;
62.     newnode->prev = ps;
63.     ps->next = newnode;
64. }
65. 
66. //判空
67. bool ListEmpty(DSLNode* ps) {
68.   assert(ps);//断言
69.   return ps->next == ps;//如果是只有一个ps头节点,则表示为空,返回true,否则返回false
70. 
71. }
72. 
73. //返回链表节点个数
74. int Listsize(DSLNode* ps) {
75.   assert(ps);
76.   int count = 0;
77.   DSLNode* cur = ps->next;
78.   while (cur != ps) {
79.     count++;
80.     cur = cur->next;
81.   }
82.   printf("\n");
83.   return count;
84. }
85. //尾部删除
86. void ListPopBack(DSLNode* ps) {
87. 
88.   assert(ps);//断言
89.   assert(!ListEmpty(ps));//先判断是否为空
90.   DSLNode* cur = ps->prev;
91.   //将cur的next值为NULL
92.   ps->prev = ps->prev->prev;
93.   ps->prev->next = ps;
94.   //这是直接略过尾部最后一个元素
95.   //跳过ps之前的一个节点,先让其左右节点相连,然后释放这个地址
96.   free(cur);
97.   cur = NULL;
98. }
99. //头部删除
100. void ListPopFront(DSLNode* ps) {
101.  assert(ps);
102.  assert(!ListEmpty(ps));
103.  DSLNode* cur = ps->next;
104.  //头删  是将头节点之后的第一个节点删除释放空间
105.  ps->next = ps->next->next;
106.  ps->next->prev = ps;
107.  free(cur);
108.  cur = NULL;
109. }
110. //查找
111. DSLNode* ListFind(DSLNode* ps, int data) {
112.  assert(ps);
113.  DSLNode* cur = ps->next;
114.  while (cur != ps) {
115.    if (cur->value == data) {
116.      return cur;
117.    }
118.  }
119.  return NULL;//NULL表示不存在
120. }
121. //插入节点
122. void ListInsert(DSLNode* pos, int data) {
123. 
124.  assert(pos);
125.  //pos三种可能
126.  //1.空链表
127.  //2.只有一个节点
128.  DSLNode* cur = pos->prev;
129.  //双向链表可以直接找到pos之前的位置,单向链表只能进行循环
130.  DSLNode* newnode = CreatNode(data);
131.  pos->prev = newnode;//把新节点newnode的位置给pos的prev
132.  newnode->prev = cur;//cur表示的是创建new节点之前的时候pos之前的结点
133.  cur->next = newnode;
134.  newnode->next = pos;
135.  //另一种不使用cur的方法
136.  //newnode->next = pos;
137.  //pos->prev->next = newnode;
138.  //newnode->prev = pos->prev;
139.  //pos->prev = newnode;
140. }
141. 
142. 
143. //删除指针
144. void ListEarse(DSLNode* pos) {
145.  assert(pos);
146.  DSLNode* cur = pos->prev;
147.  DSLNode* tail = pos->next;
148.  cur->next = tail;//两者相互指向,最后释放pos空间
149.  tail->prev = cur;
150.  free(pos);
151.  pos = NULL;
152. }
153. //摧毁链表
154. void ListDestory(DSLNode* ps) {
155.  //可以设计二级指针直接将ps地址滞空为NULL
156.  //这里还是使用一级指针
157.  assert(ps);
158.  DSLNode* cur = ps->next;
159.  while (cur != ps) {
160.    DSLNode* tail = cur->next;
161.    free(cur);
162.    cur = tail;
163.  }
164.  free(ps);
165.  ps = NULL;
166. 
167. }
168. void ListDestory2(DSLNode** ps) {
169.  assert(ps);
170.  free(ps);
171.  *ps = NULL;
172. }

3.test.c

1. #define _CRT_SECURE_NO_WARNINGS
2. #include"DSLinkList.h"
3. 
4. void test()
5. {
6.  DSLNode* phead = ListInit();//初始化
7. 
8.  ListPushBack(phead, 1);
9.  ListPushBack(phead, 2);
10.   ListPushBack(phead, 3);
11.   ListPushBack(phead, 4);
12.   ListPushBack(phead, 5);//检验尾插
13.   ListPrint(phead);//打印
14. 
15.   ListPushFront(phead, 10);
16.   ListPushFront(phead, 20);
17.   ListPushFront(phead, 30);
18.   ListPushFront(phead, 40);
19.   ListPushFront(phead, 50);//检验头插
20.   ListPrint(phead);//打印
21.   printf("%d\n", Listsize(phead));//检验返回结点个数
22. 
23.   ListPopBack(phead);
24.   ListPopBack(phead);
25.   ListPopBack(phead);
26.   ListPopBack(phead);
27.   ListPopBack(phead);//尾删
28.   ListPopFront(phead);
29.   ListPopFront(phead);
30.   ListPopFront(phead);
31.   ListPopFront(phead);
32.   ListPopFront(phead);//头删
33.   ListPrint(phead);//打印
34.   ListInsert(phead->next, 10);//pos指定结点之前插入newnode新节点
35.   ListPrint(phead);//打印
36. }
37. int main()
38. {
39.   test();
40.   return 0;
41. }

总结

我们本文主要讲解了,链表中最为复杂的带头双向循环链表的使用和代码实现,实现了头插尾插,头删尾删,初始化、打印、指定pos位置插入结点或者删除结点、寻找结点、摧毁链表等函数。并做了板书图示解释相应函数的流程,附带完整代码,以便帮助大家更好的理解。

接下来我们就要进行栈和队列的学习,本文不足之处,欢迎大佬指导一二,让我们开始新章节的学习!!!

相关文章
|
5月前
|
存储 监控 安全
企业上网监控系统中红黑树数据结构的 Python 算法实现与应用研究
企业上网监控系统需高效处理海量数据,传统数据结构存在性能瓶颈。红黑树通过自平衡机制,确保查找、插入、删除操作的时间复杂度稳定在 O(log n),适用于网络记录存储、设备信息维护及安全事件排序等场景。本文分析红黑树的理论基础、应用场景及 Python 实现,并探讨其在企业监控系统中的实践价值,提升系统性能与稳定性。
177 1
|
5月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
175 0
|
9月前
|
存储 算法 Java
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
312 10
 算法系列之数据结构-二叉树
|
9月前
|
算法 Java
算法系列之数据结构-Huffman树
Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。
266 3
 算法系列之数据结构-Huffman树
|
9月前
|
算法 Java
算法系列之数据结构-二叉搜索树
二叉查找树(Binary Search Tree,简称BST)是一种常用的数据结构,它能够高效地进行查找、插入和删除操作。二叉查找树的特点是,对于树中的每个节点,其左子树中的所有节点都小于该节点,而右子树中的所有节点都大于该节点。
391 22
|
8月前
|
存储 算法 物联网
解析局域网内控制电脑机制:基于 Go 语言链表算法的隐秘通信技术探究
数字化办公与物联网蓬勃发展的时代背景下,局域网内计算机控制已成为提升工作效率、达成设备协同管理的重要途径。无论是企业远程办公时的设备统一调度,还是智能家居系统中多设备间的联动控制,高效的数据传输与管理机制均构成实现局域网内计算机控制功能的核心要素。本文将深入探究 Go 语言中的链表数据结构,剖析其在局域网内计算机控制过程中,如何达成数据的有序存储与高效传输,并通过完整的 Go 语言代码示例展示其应用流程。
158 0
|
9月前
|
存储 监控 算法
员工电脑监控系统中的 C# 链表算法剖析-如何监控员工的电脑
当代企业管理体系中,员工电脑监控已成为一个具有重要研究价值与实践意义的关键议题。随着数字化办公模式的广泛普及,企业亟需确保员工对公司资源的合理利用,维护网络安全环境,并提升整体工作效率。有效的电脑监控手段对于企业实现这些目标具有不可忽视的作用,而这一过程离不开精妙的数据结构与算法作为技术支撑。本文旨在深入探究链表(Linked List)这一经典数据结构在员工电脑监控场景中的具体应用,并通过 C# 编程语言给出详尽的代码实现与解析。
194 5
|
10月前
|
存储 监控 算法
公司监控上网软件架构:基于 C++ 链表算法的数据关联机制探讨
在数字化办公时代,公司监控上网软件成为企业管理网络资源和保障信息安全的关键工具。本文深入剖析C++中的链表数据结构及其在该软件中的应用。链表通过节点存储网络访问记录,具备高效插入、删除操作及节省内存的优势,助力企业实时追踪员工上网行为,提升运营效率并降低安全风险。示例代码展示了如何用C++实现链表记录上网行为,并模拟发送至服务器。链表为公司监控上网软件提供了灵活高效的数据管理方式,但实际开发还需考虑安全性、隐私保护等多方面因素。
214 0
公司监控上网软件架构:基于 C++ 链表算法的数据关联机制探讨
|
存储 SQL 算法
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
|
存储 SQL 算法
LeetCode 题目 86:分隔链表
LeetCode 题目 86:分隔链表

热门文章

最新文章