当前位置:   article > 正文

数据结构学习笔记(3.线性表之循环链表)

为什么引入循环链表

本节知识点:

1.为什么选择循环链表:因为有很多生活中结构是循环的,是单链表解决不了的,比如星期、月份、24小时,对于这些循环的数据,循环链表就体现出它的优势了。

2.循环链表的结构:

循环链表就是从头结点后面开始,尾节点的next不再是NULL了,而是头结点后面的第一个链表元素,如上图。

3.如何创建一个循环链表

步骤一:

步骤二:

无论是头插法,还是尾插法都没有关系,都可以创建完成这个循环链表。

4.如何将一个单向链表改写成一个循环链表

第一步 (改写插入函数):

a.把插入位置pos的允许范围改成0~~~无穷大

	ret=( NULL != node) && ( NULL != Node) && (pos >= 0);

b.把两种方式的头插法情况加入程序,第一种是pos值为0和1的情况,如图:

这种情况分为两部:先把node插入到head和第一个元素直接,然后再把链表尾指向node元素(node表示插入元素)。

代码如下:

  1. if(node == (CircleListNode* )head)
  2. {
  3. Last =(CircleListNode* )Get_CircleListNode(lhead, lhead->length); //获得链表最后一个元素
  4. Last->next = Node; //把头插法的数据连接到 链表的最后一个元素的后面
  5. }

头插法的第二种情况,是循环链表,循环了一圈回来了,与第一种不同的是此时插入的相对位置和第一种的相对位置不一样。(其实这种方法跟普通插入是一样的) 如图:

第二步(改写删除函数):

a.也是把pos值的取值范围改成0 到 无穷大,但是同时记得判断length要大于0 ,要保证链表中有数据,不然删什么呀~~~~

 if(( NULL != lhead) && (pos > 0) && (lhead->length>0))

b.对于删除第一个元素有两种情况 这里是难点:首先要在删除链表元素的 前面 判断是否要删除第一个元素(此时的情况是pos为1的情况),然后删除链表元素,再判断是否是删除第一个元素的第二种情况(链表循环一圈后,到达链表第一个元素,此时元素的前一个链表不再是head头结点了)。如图:


代码如下:

  1. if(node == (CircleListNode* )head)
  2. {
  3. Last =(CircleListNode* )Get_CircleListNode(lhead, lhead->length);
  4. }
  5. ret = node->next;
  6. node->next = ret->next;
  7. /*判断是不是循环了一圈后回来的情况 */
  8. if((first == ret) &&(NULL == Last))
  9. {
  10. Last =(CircleListNode* )Get_CircleListNode(lhead, lhead->length);
  11. }
  12. /*判断是否要删除链表中的第一个元素*/
  13. if( Last != NULL )
  14. {
  15. Last->next = ret->next;
  16. lhead->head.next = ret->next;
  17. }

图中红笔的代码是:

  1. ret = node->next;
  2. node->next = ret->next;

图中蓝笔的代码是:

  1. if( Last != NULL )
  2. {
  3. Last->next = ret->next;
  4. lhead->head.next = ret->next;
  5. }

c.当length为0的是,即链表长度为0的时候,记得给头结点的next赋值为NULL

第三步 (改写获得链表元素函数)

a.记得把pos给成 0 到 无穷大,然后判断length链表长度是否为0 ,如果为0 就不能获取。

5.游标的引入:

在循环链表中一般可以定义一个游标,对于这样一个封装好的可复用循环链表,定义一个游标是十分方便的。例如:如果想依次获得链表中的每一个元素,利用get函数,太过低效了O(n2),想想利用这样一个游标去遍历的话,复杂度仅仅是O(n)。还有就是在循环链表中,游标可以在链表中进行转圈,例如:可以解决约瑟夫环问题。


6.指定删除链表中某一个元素的函数CircleListNode* CircleList_Del(CircleList* head,CircleListNode* node),其实也不是很高效,获得了当前游标的值的时候,再去调用CircleList_Del函数,这个轮询函数获得了pos,再去调用Del_CircleListNode然后又遍历了一边,把复杂的搞到了O(n2)。其实完全可以在找到pos的时候直接删除掉这个链表元素,这样的复杂度是O(n)。

7.我还觉得获得当前游标得值的函数CircleList_Slider的返回值有些问题,我觉得如果返回的是当前游标的上一个链表元素的值会更好,因为这个是一个单向链表,如果得到了上一个链表元素的值,就可以通过游标实现,删除啊,插入啊等高效的操作了。

本节代码:

CricleList.c:

  1. /*******************************************************************************************************
  2. 文件名:CircleList.c
  3. 头文件:CircleList.h
  4. 时间: 2013/08/17
  5. 作者: Hao
  6. 功能: 可以复用 带有增 删 改 查 功能的循环链表
  7. 难道: 1.typedef struct Str_CircleList CircleListNode; //这个结构体是链表的真身
  8. struct Str_CircleList //每一个链表元素的结构都会包含这个结构 因为当给链表元素强制类型
  9. { //转换成(CircleListNode* )的时候 其实就是要开始对每个元素中的 CircleListNode进行赋值了
  10. CircleListNode* next;
  11. };
  12. 这个链表结构在链表元素中起到的作用 是本节的难点
  13. 2.切记一个问题 就是已经是链表中元素的 千万不要再往链表中添加了 否则链表一定出现无穷的错误
  14. 3.对于pos值的问题 add、get、del三个函数中 的链表都是 从1开始的到length 0是链表头
  15. 在add函数中pos为0的时候是和pos为1的情况是一样的 都是头插法 0~~~~~无穷大
  16. 在get函数中pos为0的时候是获得链表头 地址 0~~~~~length
  17. 在del函数中pos为0的时候是无效的 del失败 1~~~~~length
  18. *******************************************************************************************************/
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <malloc.h>
  22. #include "CircleList.h"
  23. typedef struct str_list_head //这个是链表头 其实也可以当作一个没有前驱的 链表元素 元素的内容是链表长度
  24. {
  25. //CircleListNode* next;
  26. CircleListNode head; //这个参数要特别重视 每一个链表元素结构的第一个参数一定是 CircleListNode
  27. //因为在寻找链表元素后继的时候 其实就是将链表元素强制类型转换成 CircleListNode* 然后给next进行赋值 其实就是给 CircleListNode变量赋值
  28. CircleListNode* slider;
  29. int length; //链表长度
  30. }list_head;
  31. /*******************************************************************************************************
  32. 函数名: Creat_CircleListHead
  33. 函数功能:创建一个链表的链表头 并给链表头分配空间
  34. 参数: void
  35. 返回值:ret 成功返回链表头地址 失败返回NULL
  36. *******************************************************************************************************/
  37. CircleList* Creat_CircleListHead(void)
  38. {
  39. list_head* ret = NULL;
  40. ret = (list_head* )malloc( sizeof(list_head)*1 );
  41. if(NULL != ret) //malloc分配成功
  42. {
  43. ret->length = 0;
  44. //ret -> next = NULL;
  45. ret->head.next = NULL;
  46. ret->slider = NULL;
  47. }
  48. return (CircleList* )ret;
  49. }
  50. /*******************************************************************************************************
  51. 函数名:Destroy_CircleListHead
  52. 函数功能:释放一个链表头指针
  53. 参数:CircleList* head 链表头指针
  54. 返回值: ret 释放成功返回1 释放失败返回0
  55. *******************************************************************************************************/
  56. int Destroy_CircleListHead(CircleList* head)
  57. {
  58. int ret = 0;
  59. list_head* lhead = (list_head* )head;
  60. if( NULL != lhead )
  61. {
  62. free(lhead);
  63. ret = 1;
  64. }
  65. return ret;
  66. }
  67. /*******************************************************************************************************
  68. 函数名:Get_Length
  69. 函数功能:获得链表的长度
  70. 参数: CircleList* head 链表头指针
  71. 返回值: ret 成功返回链表长度 失败返回0
  72. *******************************************************************************************************/
  73. int Get_Length(CircleList* head)
  74. {
  75. int ret = 0;
  76. list_head* lhead = (list_head* )head;
  77. if( NULL != lhead )
  78. {
  79. ret = lhead -> length;
  80. }
  81. return ret;
  82. }
  83. /*******************************************************************************************************
  84. 函数名:Clean_CircleListHead
  85. 函数功能: 清空链表
  86. 参数: CircleList* head 链表头指针
  87. 返回值:ret 成功返回1 失败返回0
  88. *******************************************************************************************************/
  89. int Clean_CircleListHead(CircleList* head)
  90. {
  91. int ret = 0;
  92. list_head* lhead = (list_head* )head;
  93. if( NULL != lhead )
  94. {
  95. lhead -> length = 0;
  96. //lhead -> next = NULL;
  97. lhead -> head.next = NULL;
  98. lhead->slider = NULL;
  99. ret = 1;
  100. }
  101. return ret;
  102. }
  103. /*******************************************************************************************************
  104. 函数名:Add_CircleList
  105. 函数功能:往链表里面添加一个链表元素 如果pos的值是0(就是链表头)和1(链表的第一元素 链表元素个数是从1开始算的)都是头插法
  106. pos的值大于链表长度是尾插法 这里面pos值得注意的是 i=1 pos为a的时候 是把链表元素插入第a个元素的位置
  107. 当i=0 pos为a的时候 是把链表元素插入 第a个元素位置的后面 切忌:这里面0位置是链表头指针 从1开始是链表元素
  108. 参数: CircleList* head链表头指针 CircleListNode* Node插入元素的指针(被强制类型转化成CircleListNode*) int pos 插入位置
  109. pos的有效值范围是 从0到无穷大
  110. 返回值: ret 插入成功返回1 插入失败返回0
  111. *******************************************************************************************************/
  112. int Add_CircleList(CircleList* head, CircleListNode* Node, int pos)
  113. {
  114. int ret = 0;
  115. int i = 0;
  116. list_head* lhead = (list_head* )head;
  117. CircleListNode* node = (CircleListNode* )head;
  118. CircleListNode* Last = NULL;
  119. ret=( NULL != node) && ( NULL != Node) && (pos >= 0);
  120. if(1 == ret)
  121. {
  122. for(i=1; ( (i<pos) && (node->next != NULL) ); i++)
  123. {
  124. node = node->next;
  125. }
  126. Node -> next = node -> next;
  127. node -> next = Node;
  128. if(lhead->length == 0)//第一次插入元素的时候把游标 指向这个元素
  129. {
  130. lhead->slider = Node;
  131. }
  132. lhead -> length++; //这个一定要在后面调用 lhead->length值的前面更新
  133. /*判断是否为头插法 所谓头插法 就是pos为0和1的情况 其实也就是没有进for循环的情况 剩下的无论pos为多少 进入多少次循环都没有头插法*/
  134. if(node == (CircleListNode* )head)
  135. {
  136. Last =(CircleListNode* )Get_CircleListNode(lhead, lhead->length); //获得链表最后一个元素
  137. Last->next = Node; //把头插法的数据连接到 链表的最后一个元素的后面
  138. }
  139. }
  140. return ret;
  141. }
  142. /*******************************************************************************************************
  143. 函数名:Get_CircleListNode
  144. 函数功能:获得链表中第pos个元素位置的链表元素 链表是从1开始的 0是链表头 pos为0的时候表示get链表头
  145. 参数: CircleList* head链表头指针 int pos获得链表元素的位置 pos的有效取值范围是 1 到 length 0是链表头
  146. 返回值: CircleListNode*类型 第pos个链表元素的地址
  147. *******************************************************************************************************/
  148. CircleListNode* Get_CircleListNode(CircleList* head, int pos)
  149. {
  150. int ret = 0;
  151. int i = 0;
  152. list_head* lhead = (list_head* )head;
  153. /*本来pos应该是有上限的 但是变成了循环链表pos理论上说就可以无穷大了 但是get函数应该是在链表中有值的情况下才成立的 即(lhead->length>0)*/
  154. ret=( NULL != lhead) && (pos >= 0) && (lhead->length>0);
  155. if(1 == ret)
  156. {
  157. CircleListNode* node = (CircleListNode* )head;
  158. for(i=0; i<pos; i++) //执行 pos次 得到的是第pos位置的node
  159. {
  160. node = node->next;
  161. }
  162. return (CircleListNode*)node;
  163. }
  164. return NULL;
  165. }
  166. /*******************************************************************************************************
  167. 函数名:Del_CircleListNode
  168. 函数功能:删除链表中第pos位置的链表元素
  169. 参数: CircleList* head链表头指针 int pos删除链表元素的位置 pos是删除的链表元素的位置 跟get和add中的
  170. pos是配套的 有效取值范围依然是 1到 length 在这个函数里面由于不能删除链表头 所以pos为0的时候无效
  171. 返回值: CircleListNode* ret这个返回值很重要 因为这个删除仅仅是把链表元素踢出了链表 并没有free开辟的内存
  172. 应该通过这个返回的地址free 释放内存
  173. 删除成功返回 删除链表元素的地址 删除失败返回 NULL
  174. *******************************************************************************************************/
  175. CircleListNode* Del_CircleListNode(CircleList* head, int pos)
  176. {
  177. CircleListNode* ret = NULL;
  178. CircleListNode* Last = NULL;
  179. int i = 0;
  180. list_head* lhead = (list_head* )head;
  181. CircleListNode* first = lhead->head.next;
  182. if(( NULL != lhead) && (pos > 0) && (lhead->length>0))
  183. {
  184. CircleListNode* node = (CircleListNode* )head;
  185. for(i=1; i<pos; i++)//执行 pos次 得到的是第pos位置的node 这个方法行不通
  186. { //因为要想删除第pos位置的node 应该先找到它上一个链表元素
  187. node = node->next; //所以这里面i=1 比get函数少执行了一次 得到第pos-1位置的node
  188. }
  189. /*判断是不是 pos为1的 情况删除头节点后面的第一个元素(这个是没有进入for循环的) 跟循环一圈后的情况不一样 */
  190. /*循环一圈的是进入for循环的情况 此时的node不再是head了 而是链表最后一个元素*/
  191. if(node == (CircleListNode* )head)
  192. {
  193. Last =(CircleListNode* )Get_CircleListNode(lhead, lhead->length);
  194. }
  195. ret = node->next;
  196. node->next = ret->next;
  197. /*判断是不是循环了一圈后回来的情况 */
  198. if((first == ret) &&(NULL == Last))
  199. {
  200. Last =(CircleListNode* )Get_CircleListNode(lhead, lhead->length);
  201. }
  202. /*判断是否要删除链表中的第一个元素*/
  203. if( Last != NULL )
  204. {
  205. Last->next = ret->next;
  206. lhead->head.next = ret->next;
  207. }
  208. if( lhead->slider == ret)//如果删除的元素恰恰就是游标指向的元素 要把游标往后面移动一位
  209. {
  210. lhead->slider = ret->next;
  211. }
  212. lhead->length--; //这个一定要写在 Get_CircleListNode 后面 不然的话 pos就为0了
  213. /*判断链表是否 减到了空 如果链表中不再有元素 就把head.next赋值为NULL*/
  214. /*单向链表不需要这个的原因 是因为单向链表的最后一个元素的next就是NULL 而双向链表没有NULL的了*/
  215. if(0 == lhead->length)
  216. {
  217. lhead->head.next = NULL;
  218. lhead->slider = NULL;
  219. }
  220. }
  221. return (CircleListNode*)ret;
  222. }
  223. /*******************************************************************************************************
  224. 函数名: CircleList_Slider
  225. 函数功能:获得当前游标指向的数据
  226. 参数: CircleList* head
  227. 返回值:成功返回 CircleListNode* ret 失败返回NULL
  228. *******************************************************************************************************/
  229. CircleListNode* CircleList_Slider(CircleList* head)
  230. {
  231. CircleListNode* ret = NULL;
  232. list_head* lhead = (list_head* )head;
  233. if( (NULL != lhead)&&(NULL != lhead->slider) )//保证slider是有效的
  234. {
  235. ret = lhead->slider;
  236. }
  237. return ret;
  238. }
  239. /*******************************************************************************************************
  240. 函数名: CircleList_Reset
  241. 函数功能:重置游标 让游标指向head头节点后面的第一个元素
  242. 参数: CircleList* head
  243. 返回值:成功返回 当前游标的指向CircleListNode* ret 失败返回NULL
  244. *******************************************************************************************************/
  245. CircleListNode* CircleList_Reset(CircleList* head)
  246. {
  247. CircleListNode* ret = NULL;
  248. list_head* lhead = (list_head* )head;
  249. if(NULL != lhead)
  250. {
  251. lhead->slider = lhead->head.next;
  252. ret = lhead->slider;
  253. }
  254. return ret;
  255. }
  256. /*******************************************************************************************************
  257. 函数名: CircleList_Next
  258. 函数功能:使游标指向下一个元素
  259. 参数: CircleList* head
  260. 返回值:成功返回 前游标的指向CircleListNode* ret 失败返回NULL
  261. *******************************************************************************************************/
  262. CircleListNode* CircleList_Next(CircleList* head)
  263. {
  264. CircleListNode* ret = NULL;
  265. list_head* lhead = (list_head* )head;
  266. if((NULL != lhead)&&(NULL != lhead->slider)) //保证游标是有效的
  267. {
  268. ret = lhead->slider;
  269. lhead->slider = ret->next;
  270. }
  271. return ret;
  272. }
  273. /*******************************************************************************************************
  274. 函数名: CircleList_Del
  275. 函数功能:删除链表中的某个指定元素
  276. 参数: CircleList* head CircleListNode* node为指定的元素
  277. 返回值:成功返回 删除的链表元素 失败返回NULL
  278. *******************************************************************************************************/
  279. CircleListNode* CircleList_Del(CircleList* head,CircleListNode* node)
  280. { //这个函数主要是用来删除游标的返回值的
  281. CircleListNode* ret = NULL;
  282. list_head* lhead = (list_head* )head;
  283. int i=0;
  284. if((NULL != head)&&(NULL != node))
  285. {
  286. CircleListNode* current = (CircleListNode*)lhead;
  287. for(i=1; i<=lhead->length; i++)
  288. {
  289. if(node == current->next)
  290. {
  291. ret = current->next;
  292. break;
  293. }
  294. current = current->next;
  295. }
  296. if(NULL == ret) //说明没有找到node
  297. {
  298. printf("put error!!!\n");
  299. }
  300. else //找到了node
  301. {
  302. Del_CircleListNode(lhead,i);
  303. }
  304. }
  305. return ret;//返回删除的链表元素
  306. }


CircleList.h:

  1. #ifndef __CircleList_H__
  2. #define __CircleList_H__
  3. typedef void CircleList; //这个是为了 封装方便
  4. typedef struct Str_CircleList CircleListNode; //这个结构体是链表的真身
  5. struct Str_CircleList //每一个链表元素的结构都会包含这个结构 因为当给链表元素强制类型
  6. { //转换成(CircleListNode* )的时候 其实就是要开始对每个元素中的 CircleListNode进行赋值了
  7. CircleListNode* next;
  8. };
  9. CircleList* Creat_CircleListHead(void);
  10. int Destroy_CircleListHead(CircleList* head);
  11. int Get_Length(CircleList* head);
  12. int Clean_CircleListHead(CircleList* head);
  13. int Add_CircleList(CircleList* head, CircleListNode* Node, int pos);
  14. CircleListNode* Get_CircleListNode(CircleList* head, int pos);
  15. CircleListNode* Del_CircleListNode(CircleList* head, int pos);
  16. CircleListNode* CircleList_Del(CircleList* head,CircleListNode* node);
  17. CircleListNode* CircleList_Next(CircleList* head);
  18. CircleListNode* CircleList_Reset(CircleList* head);
  19. CircleListNode* CircleList_Slider(CircleList* head);
  20. #endif


main.c:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "CircleList.h"
  4. typedef struct _tag_str
  5. {
  6. CircleListNode head;
  7. int i;
  8. }str;
  9. int main(int argc, char *argv[])
  10. {
  11. str str1,str2,str3,str4,str5,str6;
  12. str *strp;
  13. int i=0;
  14. str1.i=1;
  15. str2.i=2;
  16. str3.i=3;
  17. str4.i=4;
  18. str5.i=5;
  19. str6.i=6;
  20. CircleList* head;
  21. head = Creat_CircleListHead();
  22. Add_CircleList(head, (CircleListNode*)&str1, 0);
  23. Add_CircleList(head, (CircleListNode*)&str2, 0);
  24. Add_CircleList(head, (CircleListNode*)&str3, 0);
  25. Add_CircleList(head, (CircleListNode*)&str4, 0);
  26. Add_CircleList(head, (CircleListNode*)&str5, 5);
  27. for(i=1; i<=2*Get_Length(head); i++)
  28. {
  29. strp = (str* )Get_CircleListNode(head, i);
  30. printf("%d\n",strp->i);
  31. }
  32. printf("\n");
  33. printf("%d\n",Get_Length(head));
  34. strp = (str* )Del_CircleListNode(head, 6);
  35. printf("%d\n",strp->i);
  36. printf("%d\n",Get_Length(head));
  37. printf("\n");
  38. for(i=1; i<=2*Get_Length(head); i++)
  39. {
  40. strp = (str* )Get_CircleListNode(head, i);
  41. printf("%d\n",strp->i);
  42. }
  43. printf("\n");
  44. printf("%d\n",Get_Length(head));
  45. strp = (str* )Del_CircleListNode(head, 1);
  46. printf("%d\n",strp->i);
  47. printf("%d\n",Get_Length(head));
  48. printf("\n");
  49. for(i=1; i<=2*Get_Length(head); i++)
  50. {
  51. strp = (str* )Get_CircleListNode(head, i);
  52. printf("%d\n",strp->i);
  53. }
  54. printf("\n");
  55. for(i=1; i<=3; i++)
  56. {
  57. strp = (str* )Del_CircleListNode(head, 1);
  58. printf("%d\n",strp->i);
  59. }
  60. /*CircleList_Reset(head);
  61. CircleList_Next(head);
  62. CircleList_Del(head,(CircleListNode*)&str3);
  63. strp = (str* )CircleList_Slider(head);
  64. printf("%d\n",strp->i);
  65. printf("\n");
  66. for(i=1; i<=2*Get_Length(head); i++)
  67. {
  68. strp = (str* )Get_CircleListNode(head, i);
  69. printf("%d\n",strp->i);
  70. }
  71. printf("\n");*/
  72. Destroy_CircleListHead(head);
  73. return 0;
  74. }




声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/545038
推荐阅读
相关标签
  

闽ICP备14008679号