1. 程式人生 > 其它 >單向環形連結串列---實現約瑟夫(Josephu)問題

單向環形連結串列---實現約瑟夫(Josephu)問題

技術標籤:資料結構與演算法設計java單鏈表連結串列

使用單向環形連結串列實現約瑟夫(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)舉例:根據使用者的輸入,生成一個小孩的順序

n=5:表示單鏈表中有五個結點,即初始時圈中有五個小孩
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