首页 > 编程笔记 > Java笔记 阅读:85

数据结构中的循环链表(Java详解版)

通义灵码
循环单链表(Circular Linked List,简称循环链表)是一种首尾相连的单链表。

在线性链表中,每个结点的指针都指向它的下一个结点,最后一个结点的指针域为空,不指向任何结点,仅表示链表结束。若把这种结构改变一下,使最后一个结点的指针域指向链表的第一个结点,就构成了循环链表。


图 1 循环单链表为空时的情况

若循环单链表带头结点为空,则 head.next==head。

有时为了操作方便,在循环单链表中只设置尾指针 rear 而不设置头指针,利用 rear 指向循环单链表的最后一个结点,如下图所示。


图 2 只设置尾指针的循环单链表

利用尾指针可以使有些操作变得简单,如图 3 所示:


图 3 设置尾指针的循环单链表LA和LB

要将两个循环单链表(尾指针分别为 LA 和 LB)合并成一个链表,只需要将一个表的表尾和另一个表的表头连接即可,如下图所示:


图 4 两个设置尾指针的循环单链表合并后

合并两个设置尾指针的循环单链表需要 3 步操作:
最后多余的 LB 的表头结点会自动被释放。

循环单链表的应用:约瑟夫问题

有 n 个人,编号为 1、2、…、n,围成一个圆圈,按照顺时针方向让编号为 k 的人从 1 开始报数,报数为 m 的人出列,他的下一个人重新开始从 1 报数,报到 m 的人出列,一直这样重复下去,直到所有的人都出列。

要求编写一个算法,输入 n、k 和 m,依次输出每次出列的人的编号。

解决约瑟夫问题可以分为 3 个步骤:
  1. 建立一个具有 n 个结点的不带头结点的循环单链表,编号从 1 到 n,代表 n 个人。
  2. 找到第 k 个结点,即第一个开始报数的人。
  3. 编号为 k 的人从 1 开始报数,并开始计数,报数为 m 的人出列,即删除该结点。从下一个结点继续开始报数,重复执行步骤(2)和步骤(3),直到最后一个结点被删除。

约瑟夫问题的算法描述如下:
  1. public void Josephus(int n, int m, int k) {
  2. //在由n个人围成的圆圈中,从第k个人开始报数,数到m的人出列
  3. ListNode p = head, q = null;
  4. for (int i = 1; i < k; i++) //从第k个人开始报数
  5. {
  6. q = p;
  7. p = p.next;
  8. }
  9. while (p.next != p) {
  10. for (int i = 1; i < m; i++) //数到m的人出列
  11. {
  12. q = p;
  13. p = p.next;
  14. }
  15. q.next = p.next; //将p指向的结点删除,即报数为m的人出列
  16. System.out.print(String.format("%4d", p.data));
  17. p = q.next; //p指向下一个结点,重新开始报数
  18. }
  19. System.out.println(String.format("%4d", p.data));
  20. }
  21.  
  22. public void CreateCycList(int n) {
  23. //创建循环单链表
  24. ListNode s, r = null;
  25. for (int i = 1; i < n + 1; i++) {
  26. s = new ListNode(i);
  27. if (head == head.next)
  28. head = s;
  29. else
  30. r.next = s;
  31. r = s;
  32. }
  33. r.next = head;
  34. }
  35.  
  36. public static void main(String args[]) {
  37. CircularLinkList L = new CircularLinkList();
  38. Scanner sc = new Scanner(System.in);
  39. System.out.print("输入圈中人的个数:");
  40. int n = sc.nextInt();
  41. System.out.print("输入开始报数的序号:");
  42. int k = sc.nextInt();
  43. System.out.print("报数为m的人出列:");
  44. int m = sc.nextInt();
  45. L.CreateCycList(n);
  46. L.Josephus(n, m, k);
  47. }
程序运行结果如下:

输入圈中人的个数:9
输入开始报数的序号:3
报数为m的人出列:5
   7   3   9   6   5   8   2   4   1

相关文章