使用java的循环单向链表解决约瑟夫问题

什么是约瑟夫问题

听说著名犹太历史学家 Josephus有过如下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,因而决定了一个自杀方式,41我的排成一个圆圈,由第1我的开始报数,每报数到第3人该人就必须自杀,而后再由下一个从新报数,直到全部人都自杀身亡为止。然而Josephus 和他的朋友并不想听从。首先从一我的开始,越过k-2我的(由于第一我的已经被越过),并杀掉第k我的。接着,再越过k-1我的,并杀掉第k我的。这个过程沿着圆圈一直进行,直到最终只剩下一我的留下,这我的就能够继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?node

约瑟夫的故事比较残忍,咱们把约瑟夫问题改成一个小游戏,游戏规则以下:测试

  假设编号为1,2,3...n我的手拉手围坐在一圈,约定编号为k(1<= k <= n)的人开始报数,数到m的人出圈,他的下一位又从1开始报数,数到m的人又出圈,依次类推,直到剩余一我的即为胜出者,由此产生一个出圈编号的序列,this

使用循环链表模拟n我的围坐一圈

首先咱们定义一个循环链表,由此来模拟n我的围坐在一圈。下面先看下单链表的实现代码spa

 1 /**
 2  * 单向循环链表
 3  */
 4 class CycleSingleLinkedList{
 5     PersonalNode first = null;
 6     /**
 7      * 向循环链表中添加num个节点
 8      * @param num
 9      */
10     public void add(int num){
11         if(num < 1){
12             System.out.printf("参数不合法");
13             return;
14         }
15         PersonalNode curr = null;
16         for (int i = 1; i <=num; i++) {
17             PersonalNode personalNode = new PersonalNode(i);
18             if(i == 1){
19                 //添加第一个节点
20                 first = personalNode;
21                 curr = first;
22                 curr.setNext(first);
23             }else {
24                 curr.setNext(personalNode);
25                 personalNode.setNext(first);
26                 curr = personalNode;
27             }
28         }
29 
30     }
31 
32 }
33 
34 /**
35  * 人物节点
36  */
37 class PersonalNode {
38     private int no;
39     private PersonalNode next;
40 
41     public PersonalNode(int no){
42         this.no = no;
43     }
44 
45     public int getNo() {
46         return no;
47     }
48 
49     public void setNo(int no) {
50         this.no = no;
51     }
52 
53     public PersonalNode getNext() {
54         return next;
55     }
56 
57     public void setNext(PersonalNode next) {
58         this.next = next;
59     }
60 }

下面咱们用图例的方式讲解一下添加n个节点的循环链表的流程,当建立第一个节点时,咱们让first和current指向第一个节点,并将current的next指向first节点,以此来造成一个循环。当添加第二个节点时,咱们将current的next指针指向第二个节点,第二个节点的next指向first,而后将current后移,即current指向第二个节点,以此类推。指针

 

如今咱们问题的第一步即n我的围坐在一块儿解决了,下面来实现第二步从第k我的开始数数。第二步比较简单,即让first指针后移k-1次,由此让first指针指向第一个数数的人。但因为first指向的节点须要出圈,这里咱们再定义一个helper指针,来指向first指针的前一个节点,即让helper指针首先指向first,依此循环直到helper的next指针等于first(由于是循环链表)。并保持helper指针始终在first指针的前一个节点(只有一个节点时与first指针指向同一个节点)。假设k=2即从第二我的开始数,m=3即数到3的人出圈,以下图所示code

 

找到node4的人出圈,这时让helper的next等于first的next,而后让first等于helper的next,以下图所示blog

 

继续从node1开始数,数到3,即node3出圈,此时指针指向状况以下图游戏

node3出圈后,下一个要出圈的为node1,依此循环,直到helper与first指针指向相同节点,即node2为最后一个要出圈的人。代码以下get

 1 /**
 2      * 约瑟夫问题
 3      * @param n n我的围成圈
 4      * @param k 第k我的开始报数
 5      * @param m 数到m的人出圈
 6      */
 7     public void joseph(int n,int k,int m){
 8         //添加n我的
 9         add(n);
10         //helper指针指向first的前一个节点
11         PersonalNode helper = first;
12         while (helper.getNext() != first){
13             helper = helper.getNext();
14         }
15         //找到第k我的
16         for (int i = 0; i < (k-1); i++) {
17             first = first.getNext();
18             helper = helper.getNext();
19         }
20         //数到m的人出圈
21         while (true){
22             if(helper == first){
23                 break;
24             }
25             for (int i = 0; i < (m-1); i++) {
26                 first = first.getNext();
27                 helper = helper.getNext();
28             }
29             System.out.printf("编号为%d的人出圈\n",first.getNo());
30             first = first.getNext();
31             helper.setNext(first);
32         }
33         System.out.println("最后出圈的人是:"+first.getNo());
34 
35     }

测试代码class

1 public static void main(String []args){
2         CycleSingleLinkedList linkedList = new CycleSingleLinkedList();
3         linkedList.joseph(4,2,3);
4 }

测试结果:

 

 

总结

以上就是实现约瑟夫问题的循环链表解决思路,固然确定还有不少别的解决思路,欢迎留言探讨,谢谢。

相关文章
相关标签/搜索