标签:时间复杂度 示例 个数 str 灵活 映射 推导 问题 元素
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
通过次数20,094提交次数33,369
class Solution { public: int lastRemaining(int n, int m) { int ans = 0; for(int i =2; i <= n; i++){ ans = (ans+m)%i; } return ans; } };
解题思路:
这其实是约瑟夫环问题,可以在剑指offer第300页找到。
这道题如果用循环队列或循环链表来模拟的话,会超时,因为对于每一次删除操作,时间复杂度都是O(n),一共要进行m次,那时间复杂度就是O(mn),而且还要开一个O(n)的空间存储数据。
这道题正确的做法应该是使用数学方法:
关于证明方法的话,可以参考剑指offer第300页,推导的关键是灵活使用 x = a%n => k*n + x = a这条公式,以及求映射p(x)的逆函数。
归纳的话:
因为每次都会从头开始找到第m个数,然后将其删除。
删除后,把其后面的那个数移动到最前面去,然后接着重复一删-移的过程。
所以我们走到最后,剩下一个元素的时候,他就是我们的答案了。
然后我们可以从最后一个元素反过来往前推导。
最后剩下一个元素的时候,这个元素怎么来的?
在剩下两个元素的时候,删掉另外一个,然后把它往前移动m个位置,它是这么来的。
所以我要复原原本的位置,那就当前位置加上m咯!
假设当前位置是idx,那么还剩两个元素的时候的位置就是idx+m
但要注意这玩意是循环的,所以idx+m可能超过长度2了,所以我们要多2取模
所以在剩下2个元素的时候,它的位置是 (idx+m)%n,其中n=2
这样就形成了递推公式: idxn = (idxn-1 + m)% n
所以我们从n=2的时候开始往n计算,特别地,idx1 = 0。
标签:时间复杂度 示例 个数 str 灵活 映射 推导 问题 元素
原文地址:https://www.cnblogs.com/olajennings/p/12601844.html