思路:
题目的主要信息:
- 从0到n-1(首尾相接)中每次去掉第m个数,下一次从去掉数的下一个开始,直到剩下最后一个数
- 返回的是最后一个数
- 有数为0的特殊情况
方法一:递归
具体做法:
n个数相后去掉第m个数,还剩下n-1个数,依然要继续去掉第m个数。由此,从(n,m)的问题变成了(n-1,m)的子问题,其中若是(n-1,m)的子问题返回的最后一个数是x,则(n,m)返回的结果就是,其中边界是n==1的时候返回0.因此,用递归解决。
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); } };
复杂度分析:
- 时间复杂度:,每个元素访问一次
- 空间复杂度:,递归栈最大深度
方法二:迭代
具体做法:
方法一的递归也可以用迭代来代替,每次记录并更新x即可。
class Solution { public: int LastRemaining_Solution(int n, int m) { if(n == 0 || m == 0) //没有小朋友的情况 return -1; int x = 0; for (int i = 2; i <= n; i++) x = (m + x) % i;//从小到大,更新x return x; } };
复杂度分析:
- 时间复杂度:,一次遍历
- 空间复杂度:,常数个变量,无额外空间
方法三:链表模拟法
具体做法:
用list模拟链表,每次遍历m个结点便删除一个,到链表尾时指定到链表头以作循环。最后
当剩下一个结点时,就是最后的结果。
class Solution { public: int LastRemaining_Solution(int n, int m) { if(n == 0 || m == 0) //没有小朋友的情况 return -1; //数组模拟法 list<int> set; for(int i = 0; i < n; i++) set.push_back(i); auto iter = set.begin(); while(set.size() > 1){ //循环找到第m个 for(int j = 0; j < m - 1; j++){ iter++; if(iter == set.end()) iter = set.begin(); } //删除第m个,并判断删除的是否为最后一个,循环 iter = set.erase(iter); if(iter==set.end()) iter = set.begin(); } return *set.begin(); } };
复杂度分析:
- 时间复杂度:,相当于遍历n次,每次m个结点
- 空间复杂度:,list链表的空间