單向環形連結串列---實現約瑟夫(Josephu)問題
使用單向環形連結串列實現約瑟夫(Josephu)問題
1.約瑟夫(Josephu)問題描述
Josephu 問題為:設編號為 1,2,… n 的 n 個人圍坐一圈,約定編號為 k(1<=k<=n)的人從 1 開始報數,數到 m 的那個人出列,它的下一位又從 1 開始報數,數到 m 的那個人又出列,依次類推,直到所有人出列為止,由此 產生一個出隊編號的序列。
2.小提示
用一個不帶頭結點的迴圈連結串列來處理 Josephu 問題:先構成一個有 n 個結點的單迴圈連結串列,然後由 k 結點起從 1 開 始計數,計到 m 時,對應結點從連結串列中刪除,然後再從被刪除結點的下一個結點又從 1 開始計數,直到最後一個結點從連結串列中刪除演算法結束。
3.約瑟夫(Josephu)問題—建立單向環形單鏈表的思路和圖解
思路:
(1)先建立一個結點,讓first指標指向該節點,並使first.next=first使得第一個結點自己 形成一個環,並將輔助指標指向該節點。核心程式碼是:
if(i==1)
{
first=boy;
//如果只有一個結點,就先自己構成一個環
first.next=first;
//讓輔助指標curBoy指向第一個小孩
curBoy=first;
}
(2)後面沒建立一個結點,就將該節點加入到該環形連結串列中,核心程式碼是
curBoy.next=boy;
boy.next=first;
curBoy=boy;
圖解:
4.約瑟夫(Josephu)問題—遍歷環形單鏈表思路
(1)建立一個輔助指標curBoy,指向first結點
(2)然後通過while迴圈遍歷單向環形單鏈表,直到curBoy.next==first結束
5.約瑟夫(Josephu)問題核心—小孩子出圈順序思路分析
(1)舉例:根據使用者的輸入,生成一個小孩的順序
k=1:表示從第一個孩子開始報數
m=2:每次數兩個人,出圈一個孩子
(2)思路分析
—建立一個helper指標,起始時指向喚醒連結串列的最後一個結點。
—小孩報數前,先讓first指標和helper指標同時移動k-1次(因為題目不一定是從第一個孩子開始報數)
—之後小孩報數時,將first指標和helper指標同時移動m-1次
----這時可以將first指向的小孩結點同時出圈:first=first.next helper.next=first;
(3)案例出圈順序為:2–>4–>1–>5–>3
6.約瑟夫(Josephu)問題程式碼實現
package cn.zzw.algorithm.LinkedList;
//使用單向環形連結串列實現約瑟夫問題
public class Josepfu {
public static void main(String[] args) {
//測試環形連結串列和遍歷
CircleSingleLinkedList circleSingleLinkedList=new CircleSingleLinkedList();
circleSingleLinkedList.addBoy(5);
circleSingleLinkedList.show();
//測試小孩結點出圈順序是否正確
circleSingleLinkedList.countBoy(1,2,5);
}
}
//建立一個Boy類,表示一個環形連結串列中的一個結點
//當然可以把屬性no和next設為私有,之後使用get和set方法進行訪問
class Boy
{
public int no;
public Boy next;//表示結點的下一個結點,預設值為null
public Boy(int no) {
this.no = no;
}
@Override
public String toString() {
return "Boy{" +
"no=" + no +
'}';
}
}
//建立一個環形的單鏈表
class CircleSingleLinkedList
{
//建立一個first結點,當前預設為null,沒有後繼結點
private Boy first=null;
//新增小孩結點,構建一個環形的連結串列
public void addBoy(int nums)
{
//為了防止nums不合法,對其進行資料校驗
if(nums<1)
{
System.out.println("nums資料值不合法");
return;
}
//使用輔助指標,幫助構建環形連結串列
Boy curBoy=null;
for(int i=1;i<=nums;i++)
{
//根據編號,建立小孩結點
Boy boy=new Boy(i);
//如果小孩是第一個結點
if(i==1)
{
first=boy;
//如果只有一個結點,就先自己構成一個環
first.next=first;
//讓輔助指標curBoy指向第一個小孩
curBoy=first;
}
else
{
curBoy.next=boy;
boy.next=first;
curBoy=boy;
}
}
}
//遍歷當前連結串列
public void show()
{
//判斷當前連結串列是否為空
if (first==null)
{
System.out.println("當前連結串列為空,沒有任何結點");
return;
}
//first指標不能動,應該使用一個輔助指標完成連結串列的遍歷
Boy curBoy=first;
while (true)
{
System.out.printf("當前小孩的編號為:%d\n",curBoy.no);
if(curBoy.next==first)
{
break;
}
//後移輔助指標
curBoy=curBoy.next;
}
}
/**
*
* @param startNo
* startNo表示從第幾個小孩開始報數
* @param countNum
* countNo表示一次數幾下
* @param nums
* nums表示一個圈中初始有幾個孩子(結點)
*/
//根據使用者輸入,指定小孩出圈的順序,也是整個約瑟夫問題的核心
public void countBoy(int startNo,int countNum,int nums)
{
//首先進行資料校驗
if(first==null||startNo<1||startNo>nums)
{
System.out.println("資料輸入有誤,請重新輸入");
return;
}
//建立輔助指標,幫助完成小孩出圈
Boy helper=first;
//開始時將輔助指標指向環形連結串列的最後一個結點
while (true)
{
if(helper.next==first)
{
break;
}
helper=helper.next;
}
//因為有時題目開始報數的並不是1,所以應該先讓first和helper同時移動startNo-1次
for (int j=0;j<startNo-1;j++)
{
first=first.next;
helper=helper.next;
}
//當小孩報數時,讓first和helper指標同時移動countNum-1次,然後出圈
//這裡是一個迴圈操作,直到圈裡面的人數只有一個
while(true)
{
if (helper==first)
{
//此時表示連結串列中只有一個結點
break;
}
//讓first指標和helper指標同時移動countNum-1次
for(int j=0;j<countNum-1;j++)
{
first=first.next;
helper=helper.next;
}
//這時first指向的就是將要出圈的孩子
System.out.printf("小孩%d出圈\n",first.no);
//這時將first指向的小孩結點出圈
first=first.next;
helper.next=first;
}
System.out.printf("最後留在圈中的小孩編號%d\n",first.no);
}
}
7.程式碼測試結果
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" "-javaagent:D:\IntelliJ IDEA\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=12960:D:\IntelliJ IDEA\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;C:\Users\1\IdeaProjects\algorithm\out\production\algorithm" cn.zzw.algorithm.LinkedList.Josepfu
當前小孩的編號為:1
當前小孩的編號為:2
當前小孩的編號為:3
當前小孩的編號為:4
當前小孩的編號為:5
小孩2出圈
小孩4出圈
小孩1出圈
小孩5出圈
最後留在圈中的小孩編號3
Process finished with exit code 0