使用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 }

測試結果:

 

 

總結

以上就是實現約瑟夫問題的循環鏈表解決思路,固然確定還有不少別的解決思路,歡迎留言探討,謝謝。

相關文章
相關標籤/搜索