題目
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
解法一(暴力法)
思路:使用數組申請一塊內存(也可以使用鏈表),按照要求循環執行n-1次移除操作,最後剩下的元素即爲所求
- 申請一塊連續內存,存放0到n的數值
- 位置索引按照題目要求計算,數組中索引從0開始,需要根據移動個數減一
- 最後一個數值即爲所求
- 時間複雜度:O(n)
- 空間複雜度:O(n)
# author: [email protected]
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
cnt, rmIdx = n, m
circle = [k for k in range(0,cnt)]
for i in range(1,n):
rmIdx = rmIdx%cnt - 1 #索引從0開始,計數從1開始
tmpVal = circle.pop(rmIdx)
cnt -= 1
rmIdx = rmIdx + m if rmIdx >= 0 else m #索引爲-1時,直接賦值m
return circle.pop()
解法二(遞歸)
思路:約瑟夫環的公式爲:,具體原理,我也沒搞懂。
思路:約瑟夫環的遞推公式爲:f(n,m) = [f(n-1,m) + m] % n,具體原理,我也沒搞懂,如果有明白的朋友,希望不吝賜教。
大概意思是,n-1個數值的最後剩餘數值向右移動m位,即得到,n個數時最後剩餘的數值(對n求餘是,在超出數值時繼續重0開始編號,爲了形成環,這個我知道),具體原因,我也沒理解。
看了一些解釋,過程是,每次刪除一個數後,從被刪位置的下一個位置,從零開始重新循環編號。
- 當只有一個數據時,返回唯一的可能情況:0
- 遞歸得到上一次刪除最後剩餘的值 x
- 上一次的結果加入後,從x作爲計數開始
- 時間複雜度:O(n)
- 空間複雜度:O(1)
# author: [email protected]
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
if n == 1:
return 0
x = self.lastRemaining(n-1,m)
return (m + x) % n
解法三(循環)
思路:將遞歸的思路,使用循環實現
- 時間複雜度:O(n)
- 空間複雜度:O(1),另外也減少了棧空間的申請
# author: [email protected]
class Solution:
def lastRemaining(self, n: int, m: int) -> int:
last = 0
for i in range(1,n):
last = (last + m) % (i + 1)
return last