"約瑟夫環"問題
約瑟夫環:在羅馬人佔領喬塔帕特後,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;
}
}