題目
【首先,讓小朋友們圍成一個大圈,小朋友的編號是從0到n-1。然後隨機指定一個數m,讓編號爲0的小朋友開始報數。每次喊到 m-1 的那個小朋友要出列唱首歌,然後可以在禮品箱中任意的挑選禮物,並且不再回到圈中,從他的下一個小朋友開始,繼續0…m-1報數…這樣下去…直到剩下最後一個小朋友,求最後一個小朋友的編號。如果沒有小朋友,請返回-1】
方法一
1、分析
- 該題是約瑟夫環問題,可以採用環形鏈表的方式來模擬圓圈。該環形鏈表共有n個節點,每次刪除第m-1個節點(從0開始計數)。
- 使用標準模板庫中的list來進行操作,每次刪除一個元素需要運行 m 步,若共有n個數字,則其時間複雜度爲 ,同時還需要一個輔助的鏈表來模擬圓圈,其空間複雜度 。
2、代碼
class Solution {
public:
int LastRemaining_Solution(int n, int m)
{
if(n<1 || m<1)
return -1;
list<int> num;
for(int i=0;i<n;++i)
num.push_back(i);
list<int>::iterator curNum=num.begin();
while(num.size()>0)
{
if(num.size()==1)
return *curNum;
//注意初始時迭代器是指向第一個元素,所以是從j=1開始,而不是0
for(int j=1;j<m;++j)
{
curNum++;
if(curNum==num.end())
{
curNum=num.begin();
}
}
list<int>::iterator nextNum=++curNum;
if(nextNum==num.end())
nextNum=num.begin();
num.erase(--curNum);
curNum=nextNum;
}
}
};
方法二:
1、分析
- 由於使用環形鏈表的方式來刪除對應的元素,每次刪除元素會有大量的重複遍歷過程,且需要藉助額外的空間,所以可以使用更好的方法。
- 當有n個元素時,刪除第m個元素,則第一個被刪除的元素爲 (因爲m可能會大於n,所以要對n取模)。剩下的爲 0,1,2······,k-1, k+1,···,n-1 。並且下一次刪除數字時從 k+1 開始計數,相當在剩下的序列中 k+1 排在最前面: k+1,···,n-1 , 0,1,2······,k-1。
- 可以將該過程看成關於 m 和 n 的函數,設: 爲在n個數字中不斷的刪除第m個數字,最後所剩下的數字。設 爲刪除第一個數字之後,所剩下的數字中再不斷的刪除第m個數字,最後所剩下的數字。顯然 。
- 接下來將 k+1,···,n-1 , 0,1,2······,k-1 這些數字映射成 0~n-2 的序列
k+1 --> 0
k+2 --> 1
·······
n-1 --> n-k-2
0 --> n-k-1
1 --> n-k
····
k-1 --> n-2
最終的迭代公式爲:
2、代碼
class Solution {
public:
int LastRemaining_Solution(int n, int m)
{
if(n<1 || m<1)
return -1;
int last=0;
for(int i=2;i<=n;++i)
{
last=(last+m)%i;
}
return last;
}
};