聽說著名猶太曆史學家 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我的圍坐在一圈。下面先看下單鏈表的實現代碼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 }
測試結果:
以上就是實現約瑟夫問題的循環鏈表解決思路,固然確定還有不少別的解決思路,歡迎留言探討,謝謝。