题目的主要信息:
  • 从0到n-1(首尾相接)中每次去掉第m个数,下一次从去掉数的下一个开始,直到剩下最后一个数
  • 返回的是最后一个数
  • 有数为0的特殊情况
举一反三:

学习完本题的思路你可以解决类似的模拟问题。

方法一:递归(推荐使用)

知识点:递归

递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。因此递归过程,最重要的就是查看能不能讲原本的问题分解为更小的子问题,这是使用递归的关键。

思路:

nn个数相后去掉第mm个数,还剩下n1n-1个数,依然要继续去掉第mm个数。由此,从(n,m)(n,m)的问题变成了(n1,m)(n-1,m)的子问题,其中若是(n1,m)(n-1,m)的子问题返回的最后一个数是xx,则(n,m)(n,m)返回的结果就是(m+x)%n(m+x)\%n,因此,用递归解决。

  • 终止条件:n=1n=1时就只剩下了最后一个孩子,应该返回0.
  • 返回值: 子问题的结果加上mm再对nn取模,就是上一级删除的元素。
  • 本级任务: 递归进入子问题获取子问题删除的元素,再推算自己这一级删除的元素。

具体做法:

  • step 1:首先判断没有小朋友的特殊情况。
  • step 2:然后递归计算子问题,并将子问题的结果xx运算(m+x)%n(m+x)\%n得到父问题的结果。

图示:

图片说明

Java实现代码:

public class Solution {
    private int function(int n, int m) {
        if (n == 1)  
            return 0;
        //递归
        int x = function(n - 1, m);
        //返回最后删除的那个元素
        return (m + x) % n;  
    }
    public int LastRemaining_Solution(int n, int m) {
        //没有小朋友的情况
        if(n == 0 || m == 0) 
            return -1;
        return function(n, m);
    }
}

C++实现代码:

class Solution {
public:
    int function(int n, int m) {
        if (n == 1)  
            return 0;
        //递归
        int x = function(n - 1, m);
        //返回最后删除的那个元素
        return (m + x) % n;  
    }
    int LastRemaining_Solution(int n, int m) {
        //没有小朋友的情况
        if(n == 0 || m == 0) 
            return -1;
        return function(n, m);
    }
};

Python实现代码:

import sys
sys.setrecursionlimit(100000) 
class Solution:
    def function(self, n: int, m: int) -> int:
        if n == 1:  
            return 0
        #递归
        x = self.function(n - 1, m)
        #返回最后删除的那个元素
        return (m + x) % n  
    
    def LastRemaining_Solution(self , n: int, m: int) -> int:
        #没有小朋友的情况
        if n == 0 or m == 0: 
            return -1
        return self.function(n, m)

复杂度分析:

  • 时间复杂度:O(n)O(n),每个元素访问一次
  • 空间复杂度:O(n)O(n),递归栈最大深度
方法二:迭代(扩展思路)

思路:

方法一的递归也我们可以用迭代来代替,递归是自顶向下找子问题,根据子问题再自顶向上返回给父问题,来推算父问题的结果。我们可以直接从小的子问题,往上推,还是根据公式(m+x)%n(m+x)\%n,只是这里的nn变成了每一级子问题的长度。

for (int i = 2; i <= n; i++)
    x = (m + x) % i;

具体做法:

  • step 1:首先判断没有小朋友的特殊情况。
  • step 2:假设最后剩余1个的时候,结果肯定为0.
  • step 3:从最后剩余1个小朋友推断2个,再不断根据公式推断到nn个。

Java实现代码:

public class Solution {
    public int LastRemaining_Solution(int n, int m) {
        //没有小朋友的情况
        if(n == 0 || m == 0) 
            return -1;
        int x = 0;
        //从小到大,更新x
        for(int i = 2; i <= n; i++)
            x = (m + x) % i;
        return x;
    }
}

C++实现代码:

class Solution {
public:
    int LastRemaining_Solution(int n, int m) {
        //没有小朋友的情况
        if(n == 0 || m == 0) 
            return -1;
        int x = 0;
        //从小到大,更新x
        for(int i = 2; i <= n; i++)
            x = (m + x) % i;
        return x;
    }
};

Python实现代码:

class Solution:
    def LastRemaining_Solution(self , n: int, m: int) -> int:
        #没有小朋友的情况
        if n == 0 or m == 0: 
            return -1
        x = 0
        #从小到大,更新x
        for i in range(2, n + 1):
            x = (m + x) % i
        return x

复杂度分析:

  • 时间复杂度:O(n)O(n),一次遍历
  • 空间复杂度:O(1)O(1),常数个变量,无额外辅助空间