题目
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: suoxd123@126.com
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
f(n,m) = [f(n-1,m)+m]\%n
f(n,m)=[f(n−1,m)+m]%n,具体原理,我也没搞懂。
思路:约瑟夫环的递推公式为:f(n,m) = [f(n-1,m) + m] % n,具体原理,我也没搞懂,如果有明白的朋友,希望不吝赐教。
大概意思是,n-1个数值的最后剩余数值向右移动m位,即得到,n个数时最后剩余的数值(对n求余是,在超出数值时继续重0开始编号,为了形成环,这个我知道),具体原因,我也没理解。
看了一些解释,过程是,每次删除一个数后,从被删位置的下一个位置,从零开始重新循环编号。
- 当只有一个数据时,返回唯一的可能情况:0
- 递归得到上一次删除最后剩余的值 x
- 上一次的结果加入后,从x作为计数开始
- 时间复杂度:O(n)
- 空间复杂度:O(1)
# author: suoxd123@126.com
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: suoxd123@126.com
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