约瑟夫环形链表问题求解
问题介绍
这里直接摘抄百度百科的问题介绍
约瑟夫问题是个有名的问题: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 }