約瑟夫問題


概念介紹

  有同學想了解約瑟夫問題,今天它來了!直接進入主題,什么是約瑟夫問題?約瑟夫問題:N個人圍成一圈,從約定編號為K的人開始報數,第M個將被殺掉,依次類推,最后剩下一個,其余人都將被殺掉

  直接上圖展示,初始化狀態: 假設n=6,總共有6個人,k=1,從第一個人開始報數,m=5,每次數五個

  

      第一次報數:從一號開始,數五個數,1-2-3-4-5,數完五個數,五號被殺死,第一次報數后,剩余人數如下

  

  第二次報數: 從被殺死的五號的下一位開始報數,也就是六號,數五個數,6-1-2-3-4,數數完畢,四號被殺死,第二次報數后,剩余人數如下

  

  第三次報數: 從被殺死的四號的下一位開始報數,同樣是六號,數五個數,6-1-2-3-6,數數完畢,六號被殺死,第三次報數后,剩余人數如下

  

  第四次報數: 從被殺死的六號的下一位開始報數,也就是一號,數五個數,1-2-3-1-2,數數完畢,二號被殺死,第四次報數后,剩余人數如下

  

  第五次報數: 從被殺死的二號的下一位開始報數,也就是三號,數五個數,3-1-3-1-3,數數完畢,三號被殺死,只剩下一號,Game Over!

  

 代碼實現

  實現思路:用單向循環鏈表來表示圈,將人殺死后修改鏈表上的節點即可。

  第一步:構建節點上對象。

 1 class Person {
 2     // 人的編號
 3     private int no;
 4     // 指向下一個人
 5     private Person next;
 6 
 7     public Person(int no) {
 8         this.no = no;
 9     }
10 
11     public int getNo() {
12         return no;
13     }
14 
15     public void setNo(int no) {
16         this.no = no;
17     }
18 
19     public Person getNext() {
20         return next;
21     }
22 
23     public void setNext(Person next) {
24         this.next = next;
25     }
26 
27 }

  第二步:創建一個環形的單向鏈表,將帶有編號的人組織起來。

class CircleSingleLinkedList {
    // 創建一個first節點,當前沒有編號
    private Person first = null;

    // 往環形鏈表中添加人,persons表示添加的人數
    public void addPerson(int persons) {
        if (persons < 1) {
            System.out.println("persons的值異常");
            return;
        }
        // 輔助指針,幫助構建環形鏈表
        Person curPerson = null;
        // 往環形鏈表中添加人
        for (int i = 1; i <= persons; i++) {
            Person person = new Person(i);
            // 如果是第一次添加人
            if (i == 1) {
                first = person;
                first.setNext(first);
                curPerson = first;
            } else {
                curPerson.setNext(person);
                person.setNext(first);
                curPerson = person;
            }
        }
    }
}

  第三步:實現數數的方法。

    /**
     *
     * @param startNo
     *            表示從第幾個人開始數數
     * @param countNum
     *            表示每次數幾下
     * @param nums
     *            表示圈中的人數
     */
    public void startCount(int startNo, int countNum, int nums) {
        if (first == null || startNo < 1 || startNo > nums) {
            System.out.println("參數輸入有誤, 請重新輸入");
            return;
        }
        // 輔助指針,當我們要退出時的依據,當helper == first時,退出
        Person helper = first;
        // 需求創建一個輔助指針(變量) helper , 事先應該指向環形鏈表的最后這個節點
        while (true) {
            if (helper.getNext() == first) {
                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 次, 然后first指向人被殺死,出圈
        // 這里是一個循環操作,直至圈中只有一個節點
        while(true) {
            // 當helper == first時,說明圈中只有一個節點
            if(helper == first) {
                break;
            }
            // 讓 first 和 helper 指針同時移動 countNum - 1
            for(int j = 0; j < countNum - 1; j++) {
                first = first.getNext();
                helper = helper.getNext();
            }
            System.out.printf("第%d號人被殺死\n", first.getNo());
            // 出圈操作
            first = first.getNext();
            helper.setNext(first);

        }
        System.out.printf("最后留在圈中的人的編號是:%d \n", first.getNo());
    }

  至此,代碼編寫完成,Git地址:https://github.com/HollowCup/algorithms-and-data-structure,具體實現位於algorithms工程下的josephus目錄,如果發現不足之處,請聯系我進行更改,十分感謝!關注我,為你揭秘更多算法!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM