约瑟夫环形链表问题求解

问题介绍

这里直接摘抄百度百科的问题介绍

约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3。

在本文的解析中,赋了另外一个初始条件k,即计数是从第k个开始的。

 

  这里以M=2,N=5,k=1为例画图说明

 

  下图中,五个节点首尾相连,每个节点的next指向下个节点。

  因为k=1,即为当前链表头部的位置,即first,所以first指针不需要移动。但是如果k≠1,那么需要通过for循环遍历链表,利用Node.number = k 条件来定位first指针的初始位置。

  其次,pointer同样是非常重要的一个指针。它的作用是用来把报数数到的节点“杀死”,即删除。它的位置永远位于first指针的前一个节点处。

  

  下面开始计数,当第一次报数时,通过循环将指针first定位到需要删除的节点上。与此同时,pointer指针也需要跟随first移动同样的长度。要时刻记住这两个指针始终是相邻的。

  注意:由于计数是从初始节点开始的,所以循环的长度为M-1,并非M。

 

   

 

  此时,链表通过循环计数两次,将first定位至Node2节点处,也就是说需要将Node2节点删除。

  可以通过以下的方法实现:

  首先,将first指针顺次后移一位,然后利用 pointer.next = first 直接将Node1指针的next赋值为Node3,失去了连接的Node2会被垃圾回收器自动回收,即完成了删除操作。

  

 

  最后,通过循环不断遍历取出数据。注意最后while循环的结束条件为pointer与first指针重合,即pointer = first。因为遍历到最后只剩下最后一个节点,此时循环结束。

 

上代码:

下面是关于节点的Bean类

 1 //创建一个Node类表示一个节点
 2 class Node {
 3     private int no; //编号
 4     private Node next;  //指向下一个节点,默认为null
 5 
 6     public Node(int no) {
 7         this.no = no;
 8     }
 9 
10     public int getNo() {
11         return no;
12     }
13 
14     public void setNo(int no) {
15         this.no = no;
16     }
17 
18     public Node getNext() {
19         return next;
20     }
21 
22     public void setNext(Node next) {
23         this.next = next;
24     }
25 }

 

下面是方法主体部分

 1     //根据用户的输入计算节点抹去的顺序
 2     public void order(int startNo, int count, int total){
 3         //先对数据校验
 4         if (first == null || startNo < 1 || startNo > total || count < 0 || total < 1){
 5             System.out.println("参数输入不合法");
 6         }
 7 
 8         //创建辅助指针pointer,用于抹去节点
 9         Node pointer = first;
10         //需要将pointer指向first节点之前的一个节点
11         while (true) {
12             if (pointer.getNext() == first) {
13                 break;
14             }
15             pointer = pointer.getNext();
16         }
17 
18         //通过循环将first和pointer指向输入对应的位置
19         for (int i = 0; i < startNo - 1; i++) {
20             first = first.getNext();
21             pointer = pointer.getNext();
22         }
23 
24         while (true) {
25             //当pointer和first指针重合时,说明环中只剩下一个节点,此时停止循环
26             if (pointer == first) {
27                 break;
28             }
29             //开始计数
30             for (int i = 0; i < count - 1; i++) {
31                 first = first.getNext();
32                 pointer = pointer.getNext();
33             }
34             System.out.printf("移除%d节点\n", first.getNo());
35 
36             //将first指向的节点抹除
37             first = first.getNext();
38             pointer.setNext(first);
39         }
40         System.out.printf("最后剩余“%d”节点", first.getNo());
41     }

 

posted @ 2021-03-21 08:05  Carln  阅读(56)  评论(0编辑  收藏  举报