"約瑟夫環"問題

"約瑟夫環"問題

約瑟夫環:在羅馬人佔領喬塔帕特後,39 個猶太人與 Josephus 及他的朋友躲到一個洞中,39 個猶太人決定寧願死也不要被敵人抓到,於是決定了一種自殺方式,41 個人排成一個圓圈,由第 1 個人開始報數,報數到 3 的人就自殺,然後再由下一個人重新報 1,報數到 3 的人再自殺,這樣依次下去,直到剩下最後一個人時,那個人可以自由選擇自己的命運。這就是著名的約瑟夫問題。現在請用單向環形鏈表得出最終存活的人的編號。

  首先設置一個長度爲n的數組,默認初始化爲false,若當前數組中的元素的值爲false,說明該元素還未被移除,若當前數組中的元素爲true,先將sum++,此時就需要對當前用於計數的sum與m值進行比較,如果sum==m,說明當前位置上的值需要被設置爲true(表示該元素已被移除),並將sum重新設置爲0,淘汰人數pass+1,若sum!=m,說明此時的sum還不到m,因此將i++,繼續判斷即可。若當前元素是false,說明當前元素已被移除,直接i++找後一個即可,循環的退出條件是當pass淘汰的人數=總人數(pass<n),此時最終返回的i即爲最後一個人所處的編號。
  由於在每次sum++後的值與m進行判斷,如果相同會將當前i位置上的元素設置爲true,再將i++,因此當前i位置上對應最終返回的其實是i+1,這也就將數組的下標與編號(從0開始)一一對應起來。當最後一個元素返回時,其返回的i並不是下標,而是下標+1.也就剛好是編號的值。


/**
 * Created by xiaoaxiao on 2019/11/27
 * Description: n個人,報到m出去,最後剩下的那個人的編號(從1開始)
 * 			時間複雜度:O(n²)		空間複雜度:O(n)
 */
public class JosephRing {

    public static int getResult(int n, int m) {
        // 設置一個數組表示這些人是否存活
        // 先假設這n個人都活着(false)
        boolean[] people = new boolean[n];
        // 設置當前的編號(1-m)
        int sum = 0;
        // 已經被淘汰了多少人
        // 通過這個已被淘汰的人和總人數的比值,進行循環的退出
        int pass = 0;
        // 定義一個i表示當前數組的下標
        int i = 0;
        while (pass < n) {
            if (i == n) {  // 需要對該數組進行循環遍歷,因此當i==n時,將i設置爲0
                i = 0;
            }
            if (!people[i]) { // 若當前位置上的人還沒有被淘汰
                sum++;
                // 若此時sum==m,說明當前這個位置上報的數就是m
                // 先++再比較,因爲sum是從0開始計數的
                if (sum == m) {
                    // 淘汰的人+1
                    pass++;
                    // 該位置被設置爲true,意味着該位置已被淘汰
                    people[i] = true;
                    // 將sum重新設置爲0,進行下一輪的約瑟夫過程
                    sum = 0;
                }
                // 無論當前位置上的人是否被淘汰,i都需要向後走一步
                // 比較巧妙:如果是最後一次pass==n時,此時的i再+1,剛好對應了實際的編號(從1開始)
                i++;
            } else { // 若當前位置上的人已經被淘汰了,直接i++往後走就行
                i++;
            }
        }
        return i;
    }
}

發佈了119 篇原創文章 · 獲贊 24 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章