前面我们学习了单向链表,现在介绍单向循环链表,单向循环链表是单链表的一种改进,若将单链表的首尾节点相连,便构成单向循环链表结构,如下图:

对于一个循环链表来说,其首节点和末节点被连接在一起。这种方式在单向和双向链表中皆可实现。要转换一个循环链表,可以选择开始于任意一个节点然后沿着列表的任一方向直到返回开始的节点。再来看另一种方法,循环链表可以被视为“无头无尾”。这种列表很利于节约数据存储缓存, 假定你在一个列表中有一个对象并且希望所有其他对象迭代在一个非特殊的排列下。指向整个列表的指针可以被称作访问指针。
    循环链表中第一个节点之前就是最后一个节点,反之亦然。循环链表的无边界使得在这样的链表上设计算法会比普通链表更加容易。对于新加入的节点应该是在第一个节点之前还是最后一个节点之后可以根据实际要求灵活处理,区别不大。当然,如果只会在最后插入数据(或者只会在之前),处理也是很容易的。
      
      循环链表的应用

一、Joseph问题(约瑟夫环)

据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人找到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

约瑟夫环用数学问题来描述就是:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。如何用循环链表来求解Josephu问题?

下面我们用单向循环链表来模拟这个问题:

[cpp] view plaincopy
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. typedef int data_t;
  4. typedef struct node_t
  5. {
  6. data_t data;
  7. struct node_t *next;
  8. }linknode_t,*linklist;
  9. linklist CreateList(int n)
  10. {
  11. int i;
  12. linklist p,head,tail;
  13. head = NULL;
  14. for(i = 1;i <= n;i++)
  15. {
  16. p = (linklist)malloc(sizeof(linklist));
  17. if(p == NULL)
  18. {
  19. printf("malloc fails!\n");
  20. }
  21. p->data = i;
  22. if(head == NULL)
  23. {
  24. head = p;
  25. tail = head;
  26. }
  27. else
  28. {
  29. tail->next = p;
  30. }
  31. tail = p;
  32. }
  33. tail->next = head;
  34. return head;
  35. }
  36. void Joseph(int n,int k,int m)
  37. {
  38. int i;
  39. linklist p,r;
  40. p = CreateList(n);
  41. for(i = 1;i < k;i++) //从第K个人开始数
  42. {
  43. p = p->next;
  44. }
  45. while(p->next != p)
  46. {
  47. for(i = 1;i <= m-2;i++)  //数到第m个人,去自杀
  48. p = p->next;
  49. r = p->next;
  50. p->next = r->next;
  51. printf("%d->",r->data);
  52. free(r);
  53. p = p->next;//从下一个人继续数
  54. }
  55. printf("%d\n",p->data);
  56. }
  57. int main()
  58. {
  59. Joseph(41,1,3);
  60. return 0;
  61. }

输出结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/linklist$ ./list1
  2. 3->6->9->12->15->18->21->24->27->30->33->36->39->1->5->10->14->19->23->28->32->37->41->7->13->20->26->34->40->8->17->29->38->11->25->2->22->4->35->16->31

我们可以看到,最后两个是16和31,这样,约瑟夫和他的朋友就躲过了一劫!

二、判断一个链表是不是循环链表(如何判定这个链表当中是否包含有环路)

解决方法:

判断是否是循环链表时,也设置两个指针,慢指针和快指针,让快指针比慢指针每次移动快两次。如果快指针追赶上慢指针,则为循环链表,否则不是循环链表,如果快指针或者慢指针指向NULL,则不是循环链表。

代码如下:

[cpp] view plaincopy
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. typedef int data_t;
  4. typedef struct node_t
  5. {
  6. data_t data;
  7. struct node_t *next;
  8. }linknode_t,*linklist;
  9. linklist CreateList(int n)
  10. {
  11. int i;
  12. linklist p,head,tail;
  13. head = NULL;
  14. for(i = 1;i <= n;i++)
  15. {
  16. p = (linklist)malloc(sizeof(linklist));
  17. if(p == NULL)
  18. {
  19. printf("malloc fails!\n");
  20. }
  21. p->data = i;
  22. if(head == NULL)
  23. {
  24. head = p;
  25. tail = head;
  26. }
  27. else
  28. {
  29. tail->next = p;
  30. }
  31. tail = p;
  32. }
  33. tail->next = head;
  34. return head;
  35. }
  36. int JudgeIsloop(linklist list)
  37. {
  38. int flag = 0;
  39. linknode_t *slow,*fast;
  40. if(list == NULL)
  41. return 0;
  42. slow = list;
  43. fast = list->next;
  44. while(slow)
  45. {
  46. if(fast == NULL || fast->next == NULL)//走到头了
  47. return 0;
  48. else if(fast == slow || fast->next == slow)//二者相遇,因为fast走的快,如果fast->next指向slow,也是循环的
  49. {
  50. flag = 1;
  51. return 1;
  52. }
  53. else
  54. {
  55. slow = slow->next;//慢指针走一步
  56. fast = fast->next->next;//快指针走两步
  57. }
  58. }
  59. return 0;
  60. }
  61. int main()
  62. {
  63. int i;
  64. int flag = 0;
  65. linklist list;
  66. list = CreateList(10);
  67. JudgeIsloop(list);
  68. if(flag = 0)
  69. printf("The list is not a looplist!\n");
  70. else
  71. {
  72. printf("The list is a looplist!\n");//循环链表则打印出来
  73. for(i = 0;i < 10;i++)
  74. {
  75. printf("%d->",list->data);
  76. list = list->next;
  77. }
  78. printf("%d\n",list->data);
  79. }
  80. return 0;
  81. }

结果如下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/linklist$ ./list2
  2. The list is a looplist!
  3. 1->2->3->4->5->6->7->8->9->10->1
  4. fs@ubuntu:~/qiang/linklist$

Linux C 数据结构—-循环链表相关推荐

  1. go移植linux内核书名叫啥,Go语言移植Linux内核数据结构hlist

    hlist(哈希链表)可以通过相应的Hash算法,迅速找到相关的链表Head及节点. 在有些应用场景,比Go标准库提供的list(一种双向链表)更合适. 依照list.h中的源码,我实现了一个Go语言 ...

  2. 数据结构——循环链表之约翰夫生死游戏

    数据结构--循环链表之约翰夫生死游戏 一.什么是约翰夫生死游戏? 约瑟夫游戏的大意是: 每30个旅客同乘一条船,因为严重超载,加上风高浪大,危险万分;因此船长告诉乘客,只有将全船一半的旅客投入还中,其 ...

  3. linux内核数据结构之链表

    1.前言 最近写代码需用到链表结构,正好公共库有关于链表的.第一眼看时,觉得有点新鲜,和我之前见到的链表结构不一样,只有前驱和后继指针,而没有数据域.后来看代码注释发现该代码来自linux内核,在li ...

  4. linux 内核 数据结构 file_operations、file、inode

    文件操作结构 将驱动程序操作连接到设备编号,结构定义在<linux/fs.h>,其中包含一组函数指针,每个打开的文件(在内部由一个file结构表示)和一组函数关联(通过包含指向一个file ...

  5. Linux创始人数据结构,Linux 通用数据结构说明

    device_driver include/linux/device.h struct device_driver { const char             * name; /* 驱动名称 * ...

  6. linux内核数据结构实现--链表、队列和哈希

    C是面向过程的语言,但是linux内核却用C实现了一套面向对象的设计模式,linux内核中处处体现着面向对象的思想. 1. 内核链表和list_entry 1.1 普通链表实现 我们在语法书上学到的链 ...

  7. 【Linux】Linux内核数据结构:IDR(redix树)

    1. 引言 最近在系统里遇到了IDR结构体,后来看了一下,是内核的一个基础结构. 这个是怎么引入的,引入是为了什么呢? 最早的时候,我们的结构体是一个类似于大结构体套小结构体. struct A {i ...

  8. linux 进程数据结构,Linux进程数据结构详解

    1.Linux的进程简介: 支持多线程的操作系统中,进程是资源分配的最小单位,线程是调度的基本单位.Linux是现代的32位或64位的支持多线程的操作系统,不过Linux是一种以轻量级进程作为线程,多 ...

  9. Linux内核数据结构——链表

    目录 目录 简介 单向链表 双向链表 环形链表 Linux内核中的链表实现 offsetof container_of container_of 第一部分 container_of 第二部分 链表初始 ...

最新文章

  1. 影像组学视频学习笔记[44(End)]-带95%置信区间的折线图、Li‘s have a solution and plan.
  2. 把委托说透(4):委托与设计模式
  3. 利用Axes3D绘制三维性能曲面
  4. 视频直播技术详解(8)直播云 SDK 性能测试模型
  5. 【解决方案】VS2017读取文件中文乱码,其他软件打开却没事
  6. Kafka消息丢失、重复消费的解决方案
  7. 【Linux】一步一步学Linux——Linux内核版本和发行版本(03)
  8. git 拉取远程其他分支代码_git切换远程分支并拉取远程分支代码
  9. python列表嵌套字典取值_我的 python 学习历程-Day05 字典/字典的嵌套
  10. dj鲜生-28-登陆验证父类的使用-Mixin类的定义
  11. make、make clean、make uninstall的使用
  12. 【原创】 Boost序列化自己手写实现简易版
  13. margin塌陷现象div盒子嵌套盒子外边距合并现象
  14. Spring ioc,aop的理解
  15. 相同数据源情况下,使用Kafka实时消费数据 vs 离线环境下全部落表后处理数据,结果存在差异...
  16. XGBoost数据训练小例子
  17. 山东法律学校97级二班计算机班,关于表彰全国三好学生、全国优秀学生干部和全国先进班集体及其标兵的决定...
  18. 74CMS 3.0 CSRF漏洞
  19. vs括号对齐和vs设置背景图片
  20. oeasy教您玩转vim - 52 - # 正则查找

热门文章

  1. leetcode 190. 颠倒二进制位(位运算)
  2. leetcode 1370. 上升下降字符串
  3. vj节点_创意编码—如何在JavaScript中创建VJ引擎
  4. css绘制正方体_设计师仅使用CSS绘制了8个标志性X战警
  5. 数据源 连接oracle
  6. Jenkins持续集成实践之java项目自动化部署
  7. Tcp与Ip协议的客户端和服务器编程
  8. django——url(路由)配置
  9. 有符号位和无符号位。——int8疑问有感
  10. Maven学习-目录结构