[劍指Offer]面試題62:圓圈中最後剩下的數字(約瑟夫環問題)

0,1,n-1這n個數字排成一個圓圈,從數字0開始,每次從這個圓圈裏刪除第m個數字。求出這個圓圈裏剩下的最後一個數字。
例如,0、1、2、3、4這5個數字組成一個圓圈,從數字0開始每次刪除第3個數字,則刪除的前4個數字依次是2、0、4、1,因此最後剩下的數字是3。
示例 1
輸入: n = 5, m = 3
輸出: 3
示例 2
輸入: n = 10, m = 17
輸出: 2
限制
1 <= n <= 10^5
1 <= m <= 10^6
思路
這道題目一般分爲兩種解法:模擬法和數學法。
模擬法顧名思義,按照題目給出的要求模擬一個循環鏈表,依次找出第m個數字,直至剩一個數字爲止。
數學法相對難理解一點,需要分析每次被刪除數字的規律,然後計算出最後剩下的數字。

  • 從n個數字取出第m個數字,記爲f(n,m)
  • 在n個數字中,記第一個被刪除的數字k=(m-1)%n,這裏之所以要對n取模,是要考慮到m>n的輸入。
  • 取出一個數字後,剩下的數字組成了一個新的循環鏈表,k+1成爲了鏈表的起點,舊鏈表與新鏈表的映射如下:
k+1 0
k+2 1
n-1 n-k-2
0 n-k-1
1 n-k
k-1 n-2
  • 映射p(x)=(x-k-1)%n,此時k+1爲第一個數字,即前k-1個數字接到了隊尾,因此新鏈表的序號都減去k-1,然後對n取模,保證序號不超過n-1,需要注意的是這裏涉及到了負數取模,而不同語言對負數取模的結果是不一樣的負數取模)。
  • p的逆映射p1p^{-1}(x)=(x+k+1)%n,也可以直接根據上面的映射推導出此式。
  • 此時,對n-1個數字取出第n個數字,記爲f’(n-1,m),根據映射關係可以進行如下推導:
    f(n1,m)=p1[f(n1,m)]=[f(n1,m)+k+1] f'(n-1,m)=p^{-1}[f(n-1,m)]=[f(n-1,m)+k+1]%n
    代入k=(m-1)%n
    f(n,m)=f(n1,m)=[f(n1,m)+m] f(n,m)=f'(n-1,m)=[f(n-1,m)+m]%n
    根據上式,可以用遞歸或循環計算粗最後剩下的數字。

代碼

# LeetCode, Python3
class Solution:
    def lastRemaining(self, n: int, m: int) -> int:
        if n == 1:
            return 0
        last = 0
        for i in range(2,n+1):
            last = (last + m) % i
        return last
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章