数据结构:循环链表解决约瑟夫问题
- 約瑟夫問題
- 問題來歷
- 循環(huán)鏈表進行模擬
- 思路
約瑟夫問題
問題來歷
據(jù)說著名猶太歷史學家Josephus有過以下的故事:在羅馬人占領喬塔帕特后,39 個猶太人與Josephus及他的朋友躲到一個洞中,39個猶太人決定寧愿死也不要被敵人抓到,于是決定了一個自殺方式,41個人排成一個圓圈,由第1個人開始報數(shù),每報數(shù)到第3人該人就必須自殺,然后再由下一個重新報數(shù),直到所有人都自殺身亡為止。然而Josephus 和他的朋友并不想遵從。首先從一個人開始,越過k-2個人(因為第一個人已經被越過),并殺掉第k個人。接著,再越過k-1個人,并殺掉第k個人。這個過程沿著圓圈一直進行,直到最終只剩下一個人留下,這個人就可以繼續(xù)活著。問題是,給定了和,一開始要站在什么地方才能避免被處決。Josephus要他的朋友先假裝遵從,他將朋友與自己安排在第16個與第31個位置,于是逃過了這場死亡游戲。
循環(huán)鏈表進行模擬
因為約瑟夫問題是一個環(huán)狀,因此又被稱為約瑟夫環(huán),結構與循環(huán)鏈表相似,因此可以用循環(huán)鏈表進行模擬。
思路
-
創(chuàng)建節(jié)點
class Person {//序號private int num;//后繼指針,指向下一個節(jié)點private Person next;public Person(int num) {this.num = num;}public int getNum() {return num;}public void setNum(int num) {this.num = num;}public Person getNext() {return next;}public void setNext(Person next) {this.next = next;} } -
創(chuàng)建一個單向的環(huán)形鏈表
-
先創(chuàng)建第一個頭節(jié)點,并讓first指針指向該節(jié)點,next指針指向本身,形成環(huán)狀。
-
后面每創(chuàng)建一個新的節(jié)點,就把其加到已有的環(huán)形鏈表中。
/*** 單向的環(huán)形鏈表*/
class CircleSingleLinkedList
{/*** 創(chuàng)建一個first節(jié)點*/private Person first;/*** 添加節(jié)點構建環(huán)形鏈表* @param nums 要創(chuàng)建的節(jié)點個數(shù)*/public void addPerson(int nums){if (nums < 1){System.out.println("nums值不正確!");return;}//幫助構建環(huán)形鏈表Person temp = null;//使用for來創(chuàng)建我們的環(huán)形鏈表for (int i = 1; i <= nums ; i++){//根據(jù)編號創(chuàng)建節(jié)點Person person = new Person(i);//頭節(jié)點if (i == 1){first = person;//構成環(huán)狀first.setNext(first);//讓指針指向第一個節(jié)點temp = first;continue;}temp.setNext(person);person.setNext(first);temp = person;}}
}
-
遍歷環(huán)形鏈表
- 先讓一個輔助指針temp指向first頭節(jié)點
- 通過while循環(huán)遍歷該環(huán)形鏈表 public void list() {if (first == null){System.out.println("空鏈表");return;}//頭節(jié)點不能動,用輔助指針來實現(xiàn)遍歷Person temp = first;while (true){System.out.printf("編號:%d\n",temp.getNum());//遍歷完畢if (temp.getNext() == first){break;}//指針后移temp = temp.getNext();} }
-
計算各個節(jié)點出表順序
-
創(chuàng)建一個輔助指針end指向鏈表的最后一個節(jié)點
-
根據(jù)起始報數(shù)位置startNum,讓first和end指針同時移動startNum-1次
-
開始報數(shù),每次報數(shù)讓first和end指針向后移動countNums-1次
-
最后first指針指向的節(jié)點就是要出表的節(jié)點
-
移出該節(jié)點,重復34步驟。
/*** 根據(jù)用戶的輸入計算出人出圈的順序* @param startNum 表示從第幾個人開始報數(shù)* @param countNums 表示每次數(shù)幾次* @param personNums 表示最初有多少人*/
public void play(int startNum,int countNums,int personNums)
{if (first == null || startNum < 1 || startNum > personNums){System.out.println("參數(shù)輸入有誤,請重新輸入");return;}//創(chuàng)建輔助指針Person end = first;//開始報數(shù)之前讓end指針指向最后一個節(jié)點while (true){if (end.getNext() == first){break;}end = end.getNext();}//報數(shù)前讓first和end根據(jù)startNum進行校準for (int i = 0; i < startNum - 1; i++){first = first.getNext();end = end.getNext();}//報數(shù)時,讓first和end指針同時移動(countNums-1)次while (true){//圈中只有一個節(jié)點if (end == first){break;}for (int i = 0; i < countNums - 1; i++){first = first.getNext();end = end.getNext();}System.out.printf("出圈號碼:%d\n",first.getNum());first = first.getNext();end.setNext(first);}System.out.printf("最后留在圈中的號碼是:%d\n",first.getNum());
}
測試
public class Josephu {public static void main(String[] args) {CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();//添加節(jié)點circleSingleLinkedList.addPerson(6);//遍歷鏈表circleSingleLinkedList.list();//計算出圈順序并輸出circleSingleLinkedList.play(1,5,6);} } ------------------------------------------------------------------------------------------------------------------- //輸出結果: 編號:1 編號:2 編號:3 編號:4 編號:5 編號:6 出圈號碼:5 出圈號碼:4 出圈號碼:6 出圈號碼:2 出圈號碼:3 最后留在圈中的號碼是:1 5->4->6->2->3->1如有錯誤或不足歡迎評論指正。
總結
以上是生活随笔為你收集整理的数据结构:循环链表解决约瑟夫问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构:单向链表的反转
- 下一篇: 数据结构:栈实现简易计算器