面試題62. 圓圈中最後剩下的數字

解題思路

約瑟夫環問題:N個人圍成一圈,第一個人從1開始報數,報M的將被殺掉,下一個人接着從1開始報。如此反覆,最後剩下一個,求最後的勝利者。
遞推公式:F(n, m) = (F(n-1, m) + m) % n。
F(n, m) 表示有n個人參與報數,每次將第m個人殺掉,最後勝利者的數組下標,其中n表示參與報數的總人數,m表示報到第m個人時將其殺掉。
推理步驟:
1、當n=1時,那麼他就是勝利者,數組下標爲0,其編號爲1;
2、每殺掉一個人,這個人的下一個重新由1開始報數,原本其編號爲m+1(數組下標爲m),重新開始報數後,編號就變成了1(數組下標爲0),相當於向前移動了m位。假設已知總人數爲n-1時,勝利者的數組下標爲F(n-1, m),那麼總人數爲n時,其下標應向後移動m位,即 F(n, m) = F(n-1, m) + m,由於可能數組越界,所以還要模上當前人數n,所以 F(n, m) = [F(n-1, m) + m] % n。
(注:人員編號是從1開始的,而數組下標是從0開始的)

代碼

解法一:數學+迭代

複雜度分析:
時間複雜度:O(n),需要求解的函數值有 n 個。
空間複雜度:O(1),只使用常數個變量。

class Solution:
    def lastRemaining(self, n: int, m: int) -> int:
        res = 0
        for i in range(2, n+1):
            res = (res+m)%i
        return res

解法二:數學+遞歸

複雜度分析:
時間複雜度:O(n),需要求解的函數值有 n 個。
空間複雜度:O(n),函數的遞歸深度爲 n,需要使用 O(n) 的棧空間。

# Python 默認的遞歸深度不夠,需要手動設置
sys.setrecursionlimit(100000)

def f(n, m):
    if n == 0:
        return 0
    x = f(n - 1, m)
    return (m + x) % n

class Solution:
    def lastRemaining(self, n: int, m: int) -> int:
        return f(n, m)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章