java單向環形鏈表實現約瑟夫問題

一. 概念介紹: 

什麼是約瑟夫問題?
josephu(約瑟夫) 問題爲:設編號爲1,2,3....n的n個人圍坐一圈,約定編號爲k(1<=k<=n)的人從1開始報數,數到m的那個人出列,他的下一位又從1開始報數,數到m的那個人又出列,以此類推,直到所有人出列爲止,由此產生一個出隊編號的序列。

約瑟夫問題又稱丟手帕問題:假設一羣(5個)小孩圍坐一圈,從第1個小孩開始報數,每次數2下,數到2的小孩退出遊戲,他的下一位又從1開始報數,以此類推,直到所有小孩退出遊戲爲止,這樣就會產生一個小孩退出遊戲的序列。

下面用畫圖來說明約瑟夫問題:

五個小孩圍圈坐在一起

 

從小孩1開始報數,小孩1報數 “1”,小孩2報數 “2” ,因爲報數到2,所以根據遊戲規則,小孩2退出遊戲。

此時退出序列:小孩2 --->

小孩3報數 “1” ,小孩4報數 “2” , 因爲報數到2,所以根據遊戲規則,小孩4退出遊戲。

此時退出序列:小孩2 ---> 小孩4 --->

 小孩5報數 “1” ,小孩1報數 “2” , 因爲報數到2,所以根據遊戲規則,小孩1退出遊戲。

此時退出序列:小孩2 ---> 小孩4 ---> 小孩1 --->

小孩3報數 “1” ,小孩5報數 “2” , 因爲報數到2,所以根據遊戲規則,小孩5退出遊戲。 

此時退出序列:小孩2 ---> 小孩4 ---> 小孩1 ---> 小孩5 --->

最後小孩3自己跟自己報數,小孩3報數 “1” ,小孩3報數 “2” , 因爲報數到2,所以根據遊戲規則,小孩3退出遊戲。

 此時退出序列:小孩2 ---> 小孩4 ---> 小孩1 ---> 小孩5 ---> 小孩3

二. 構建一個單向的環形鏈表

單向環形鏈表構建思路如下:

當只有一個節點的時候,節點1的next指向自己,形成自己環形鏈表。

當新添加一個節點進來時,將節點1的next指向節點2 ,節點2的next指向節點1,形成環形鏈表。

 

當再添加一個節點過來時,將節點2的next指向節點3 ,節點3的next指向節點1,形成環形鏈表。

當第4個,第5個.....第n個節點添加進來時,重複上面的過程。

以下用代碼構建一個單向環形鏈表:

class  Boy{

    private int no;

    private Boy next;

    public Boy(int no) {
        this.no = no;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public Boy getNext() {
        return next;
    }

    public void setNext(Boy next) {
        this.next = next;
    }
}
//創建一個環形的單向鏈表
class CircleSingleLinkedList{

    private Boy first = null;

    //添加小孩節點,構建一個環形的鏈表
    public void addBoy(int nums){
        if(nums < 1){
            System.out.println("nums不正確");
            return;
        }
        Boy curBoy = null;//輔助指針,幫助構建環形鏈表
        //使用for 來創建環形鏈表
        for (int i = 1 ; i <= nums ; i++){
            Boy boy = new Boy(i);
            //如果是第一個小孩
            if(i == 1){
                first = boy;
                first.setNext(first);
                curBoy = first;
            }else{
                curBoy.setNext(boy);
                boy.setNext(first);
                curBoy = boy;
            }
        }
    }
    //遍歷當前的環形鏈表
    public void showBoy(){
        if (first == null){
            System.out.println("沒有小孩");
            return;
        }
        Boy curBoy = first;
        while(true){
            System.out.printf("小孩編號 %d \n",curBoy.getNo());
            if (curBoy.getNext() == first){//說明已經遍歷完
                break;
            }
            curBoy = curBoy.getNext();
        }

    }

}

測試:

public class Josephu {

    public static void main(String[] args) {
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addBoy(5);//加入5個小孩節點
        circleSingleLinkedList.showBoy();
    }
}

結果:

在類 CircleSingleLinkedList添加方法countBoy,實現小孩丟手帕:

/**
     *
     * @param startNo 表示從第幾個小孩開始數數
     * @param countNum 表示數幾下
     * @param nums 表示最初有多少小孩在圈中
     */
    public void countBoy(int startNo, int countNum, int nums) {
        // 先對數據進行校驗
        if (first == null || startNo < 1 || startNo > nums) {
            System.out.println("參數輸入有誤, 請重新輸入");
            return;
        }
        //創建要給輔助指針,幫助完成小孩出圈
        Boy helper = first;//需求創建一個輔助指針(變量) helper , 事先應該指向環形鏈表的最後這個節點
        while (true) {
            if (helper.getNext() == first) {// 說明 helper 指向最後小孩節點
                break;
            }
            helper = helper.getNext();
        }
        //小孩報數前,先讓 first 和 helper 移動 k - 1 次
        for(int j = 0; j < startNo - 1; j++) {
            first = first.getNext(); helper = helper.getNext();
        }

        //當小孩報數時,讓 first  和 helper  指針同時 的移動	m	- 1  次,  然後出圈
        //這裏是一個循環操作,知道圈中只有一個節點
        while(true) {
            if(helper == first) { //說明圈中只有一個節點
                break;
            }
            //讓 first 和 helper 指針同時 的移動 countNum - 1
            for(int j = 0; j < countNum - 1; j++) {
                first = first.getNext();
                helper = helper.getNext();
            }
            //這時 first 指向的節點,就是要出圈的小孩節點
            System.out.printf("小孩%d 出圈\n", first.getNo());
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.printf("最後留在圈中的小孩編號%d \n", first.getNo());
    }

測試:

public static void main(String[] args) {
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addBoy(5);//加入5個小孩節點
        circleSingleLinkedList.showBoy();

        System.out.println("測試一把......");
        circleSingleLinkedList.countBoy(1, 2, 5); // 2->4->1->5->3
    }

結果:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章