題目:有n
個人,圍成乙個環,編號為 0、1、2、3、、、n-1,從第乙個人開始迴圈報數(從1
開始),假設數到m
的那個人出列,然後從下乙個人繼續數數,數到m
出列,以此迴圈,最後那個人為勝利者,求勝利者的編號。
這其實就是有名的約瑟夫問題。
可以使用陣列或者鍊錶來模擬這n個人,每次刪除第m個人,直到只剩下乙個人為止。
使用陣列刪除元素的複雜度較高,鍊錶是最優選擇,c++ stl中的鍊錶用的不太熟練,所以自己寫乙個。為了刪除方便,選擇雙向鍊錶,單向鍊錶刪除的時候還需要兩個指標,很麻煩。
class solution ; // 鍊錶節點
int lastremaining_solution(int n, int m)
//找到了第m個 開始刪除
head->parent->next = head->next;
head->next->parent = head->parent;
head = head->next;
num --; // 剩餘人數減1
}return head->val;}};
這種演算法時間複雜度為o(mn),空間複雜度為o(n)。
感覺應該存在某種規律,但我等數學渣渣並不能夠推導出來。
看完書上的解答,表示有點繞,現在用自己的話解釋一遍:
f(n,m)
:表示 每次在n
個數字0,1,...,n-1中刪除第m
個數字最後剩下的數字(也就是要求的結果)。(注意,要求數字標號需要是連續的,所以後面刪除乙個元素後標號不連續了,需要重新標號)。
在這n
個數字中,第乙個被刪除的數字是(m-1)%n
(取餘的原因是m
可能比n
大),記作k
,則k=(m-1)%n
。刪除後的序列為 0,1,...,k-1,k+1,...,n-1。由於下一次刪除是從k+1開始計數的,所以相當於從標號為k+1,k+2,...,n-1,0,1,2,...,k-1的序列中繼續刪除第m個數字,最終剩下的數字就是結果。
剩下的n-1
個數字如果重新按順序標號得到序列0,1,...,n-2,則每次刪除第m
個數,最後剩下的數字就是f(n-1,m)
。由於重新標號了,所以並不是f(n,m)=f(n-1,m)
! 那麼f(n-1,m)
對應的數字在修改標號之前是什麼數呢?
事實上,原先的不連續的序列a(k+1,k+2,...,n-1,0,1,2,...,k-1)變成了序列b(0,1,...,n-2),而我們主要是想知道如何從序列b中的某個數找到序列a中對應的關係,先建立個對映**:
b序列序列a
0k+1
1k+2
n-k-2
n-1n-k-1
0n-k
1n-2
k-1f(n-1,m)
(f(n-1,m)+k+1)%n
根據**,可以很直觀的看出,在b序列中數字x
,對應於a序列中的(x+k+1)%n
(注意:必須取餘數,因為b序列中為n-k
,而n-k+k+1
為n+1
,必須取餘數才能得到1
)。所以在b序列中標號為f(n-1,m)
,對於在a序列中就為(f(n-1,m)+k+1)%n
。
還記得k
嗎,k
就是在第一次刪除的時候刪掉的數(與n
有關的變數),k=(m-1)%n
。
將其帶入上面的式子,就得到:
(f(n-1,m)+k+1)%n = (f(n-1,m)+(m-1)+1)%n = (f(n-1,m)+m)%n
因此,我們就得到了這個遞迴公式,而當n=1
的時候,也就是序列中只有標號為0的數字,顯然最後剩下的數字就是0
,所以整個公式就是:
根據這個公式,寫**就很簡單了。
class solution
};
這種演算法的時間複雜度是o(n),空間複雜度是o(1),遠遠優於第一種演算法,但是推導複雜,數學渣渣表示如果沒見過這個題,絕對推導不出來。。。
使用鍊錶模擬的常規解法必須掌握,數學推導,那就看狀態吧。。
劍指offer第二版--面試題62
劍指offer刷題 圓圈中最後剩下的數字
目錄 問題重述 思路解析 1 實現 1 思路解析 2 實現 2 題目 0,1,n 1這n個數字排成乙個圓圈。從數字0開始每次從這個圓圈裡刪除第m個數字。求出這個圓圈裡剩下的最後乙個數字。用乙個鍊錶來儲存元素,當遍歷到鍊錶尾部的時候,讓迭代器再次指向頭部,由此形成乙個環。我們就在鍊錶中去遍歷,每次遇到...
劍指Offer 圓圈中最後剩下的數
每年六一兒童節,牛客都會準備一些小禮物去看望孤兒院的小朋友,今年亦是如此。hf作為牛客的資深元老,自然也準備了一些小遊戲。其中,有個遊戲是這樣的 首先,讓小朋友們圍成乙個大圈。然後,他隨機指定乙個數m,讓編號為0的小朋友開始報數。每次喊到m 1的那個小朋友要出列唱首歌,然後可以在禮品箱中任意的挑選禮...
劍指offer 圓圈中最後剩下的數字
例如,0,1,2,3,4這五個數字組成的圓圈,每次從數字0開始刪除第3個元素,依次刪除的數為2,0,4,1,那麼剩下的為3。思路1 可以用stl模板庫裡面的list來模擬這個環形的鍊錶,因為list是線性的,所以為了實現環形的鏈,可以在遍歷到鏈的末尾的時候跳轉到鏈的開頭,這樣就相當於乙個環形鍊錶了,...