单向环形链表:约瑟夫环问题

问题描述:

设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由 此产生一个出队编号的序列。

解决方案:

用一个不带头结点的循环链表来处理 Joseph 问题:先构成一个有 n 个结点的单循环链表,然后由 k 结点起从 1 开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直到最后一个结点从链表中删除算法结束。

解决思路:

1、构建一个单向环形链表

1. 先创建第一个节点, 让 first 指向该节点,并形成环形
2. 后面当我们每创建一个新的节点,就把该节点,加入到已有的环形链表中即可.

2、遍历环形链表

  1. 先让一个辅助指针(变量) curBoy,指向first节点
  2.  然后通过一个while循环遍历该环形链表即可 , curBoy.next == first  时结束

 

 

 

3、约瑟夫环出圈的实现☆

  1. 需要创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点.
  2. 小孩报数前,先让 first 和 helper 移动 k - 1次
  3. 当小孩报数时,让first 和 helper 指针同时 的移动 m - 1 次
  4. 这时就可以将first 指向的小孩节点出圈:
  5. first = first .next
  6. helper.next = first
  7. 原来first 指向的节点就没有任何引用,就会被回收

 

 

 

4、举例:

根据用户的输入,生成一个结点出圈的顺序
n = 5 , 即有5个结点
k = 1, 从第一个结点开始数
m = 2, 数2下
出圈的顺序
2->4->1->5->3

 

代码实现:

1. // 创建一个环形的单向链表  
2. class circlelinkedlist {  
3.     boy first = null;// 创建一个 first 节点,当前没有编号  
4.   
5.     //添加节点,构建成一个环形的链表  
6.     public void addByNums(int nums) {  
7.         // nums 做一个数据校验  
8.         if (nums < 0) {  
9.             System.out.println("addByNum():传入参数有误。。。");  
10.             return;  
11.         }  
12.         boy temp = null// 辅助指针,帮助构建环形链表  
13.         for (int i = 1; i <= nums; i++) {  
14.             boy boy = new boy(i);// 根据编号,创建小孩节点  
15.             if (i == 1) {// 如果是第一个小孩  
16.                 first = boy;  
17.                 first.setNext(first);//构成环  
18.                 temp = first;  
19.             } else {  
20.                 temp.setNext(boy);  
21.                 boy.setNext(first);//构成环  
22.                 temp = boy;  
23.             }  
24.   
25.         }  
26.     }  
27.   
28.     //遍历当前的环形链表  
29.     public void print() {  
30.         if (first == null) {  
31.             System.out.println("print():循环链表为空。。。。");  
32.             return;  
33.         }  
34.         boy temp = first;// 因为 first 不能动,因此我们仍然使用一个辅助指针完成遍历  
35.         while (true) {  
36.             int id = temp.getId();  
37.             System.out.println(id);  
38.             temp = temp.getNext();  
39.             if (temp == first) { // 说明已经遍历完毕  
40.                 break;  
41.             }  
42.         }  
43.     }  
44.   
45.     //josephu: 根据用户的输入,计算出小孩出圈的顺序  
46.     /** 
47.      * @param startNum 开始的编号(从第几个开始数) 
48.      * @param countNum 数几下 
49.      * @param nums     一共多少数(环形链表中一共有几个节点) 
50.      */  
51.     public void josephu(int startNum, int countNum, int nums) {  
52.         // 先对数据进行校验  
53.         if (first == null) {  
54.             System.out.println("Josephu():链表为空。。。");  
55.             return;  
56.         }  
57.         if (startNum <= 0 || startNum > nums || countNum == 0) {  
58.             System.out.println("输入参数有误");  
59.         }  
60.         // 创建要给辅助指针,帮助完成出圈  
61.         boy temp = first;// 创建一个辅助指针(变量) temp  
62.         // 事先应该将其指向环形链表的最后这个节点  
63.         while (true) {  
64.             if (temp.getNext() == first) {// 说明 temp 指向最后一个节点  
65.                 break;  
66.             }  
67.             temp = temp.getNext();  
68.         }  
69.   
70.         //将first指向开始数的编号(初始在第一个节点),temp也相应移动(移动startNum-1次)  
71.         for (int i = 0; i < startNum - 1; i++) {  
72.             first = first.getNext();  
73.             temp = temp.getNext();  
74.         }  
75.         //当小孩报数时,让 first和temp指针同时的移动 count- 1 次, 然后出圈  
76.         //这里是一个循环操作,直到圈中只有一个节点  
77.         while (true) {  
78.             if (temp == first) {//说明圈中只有一个节点  
79.                 break;  
80.             }  
81.             //让 first 和 helper 指针同时 的移动( countNum - 1)次  
82.             for (int i = 0; i < countNum - 1; i++) {  
83.                 first = first.getNext();  
84.                 temp = temp.getNext();  
85.             }  
86.             //这时 first 指向的节点,就是要出圈的节点  
87.             System.out.println(first.getId());  
88.             first = first.getNext();//将 first 指向出圈节点的下一个节点  
89.             temp.setNext(first);//将temp的next指向现在的first,则出圈的节点无引用,被GC  
90.         }  
91.         System.out.println(temp.getId());//最后一个节点输出  
92.     }  
93. }  

 

posted @ 2020-10-16 10:12  白刃天使  阅读(250)  评论(0编辑  收藏  举报