文章目录

    • 题目
    • 解法一(暴力法)
    • 解法二(递归)
    • 解法三(循环)

 


题目

NO. 1579

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次移除操作,最后剩下的元素即为所求

  1. 申请一块连续内存,存放0到n的数值
  2. 位置索引按照题目要求计算,数组中索引从0开始,需要根据移动个数减一
  3. 最后一个数值即为所求
  • 时间复杂度: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开始编号,为了形成环,这个我知道),具体原因,我也没理解。

看了一些解释,过程是,每次删除一个数后,从被删位置的下一个位置,从零开始重新循环编号。

  1. 当只有一个数据时,返回唯一的可能情况:0
  2. 递归得到上一次删除最后剩余的值 x
  3. 上一次的结果加入后,从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