剑指 Offer 62. 圆圈中最后剩下的数字 - 力扣(LeetCode) (leetcode-cn.com)
目录
运行结果
分析
删除第m个数字
初始序列:
0, 1, ..., n-1 (1)
我们把从序列(1)中按照规则不断删除第m个数字 最后剩下的数字记作f(n, m)
进行一次操作,我们删除掉的数字:k = (m-1)%n
剩余的序列:0, 1, ..., k-1, k+1, ..., n-1
序列重组
按照规则,下次从k+1开始计数,于是我们把该序列重组,得到:
k+1, ..., n-1, 0, 1, ..., k-1 (2)
该序列可以看作是把以下序列整体 % n 之后得到的序列
k+1, ..., n-1, n, n+1, ..., n+k-1
序列(2)有n-1项,我们把从该序列中不断按照规则删除第m个数字之后剩下的数字记作g(n-1, m)则:
f(n, m) == g(n-1, m)
映射
我们对序列(2)做一个映射,得到序列(3):
k+1 -> 0
...
n-1 -> n-k-2
0 -> n -> n-k-1
...
k-1 -> n+k-1 -> n-2
0,1,...,n-2 (3)
我们把这种映射记作p(x),则p(x) = (x - k - 1) % n
该映射的逆映射为:q(x) = (x + k + 1) % n
序列(3)有n-1项,且和序列(1)具有同样的形式,因此从序列(3)中按照规则不断删除第m个元素后剩下的数字为 f(n-1, m)
根据序列(2)和序列(3)之间的映射关系,我们可以得出:
g(n-1, m) == q( f(n-1, m) )
那么就有:
f(n, m) == q( f(n-1, m) )
因此我们就得到了:
递推关系
f(n, m)
= q( f(n-1, m) )
= ( f(n-1, m) + k + 1 ) % n
= ( f(n-1, m) + (m-1)%n + 1 ) % n
= ( f(n-1, m) + m ) % n
如果初始序列中只有一个元素0,那么按照规则,0就是最终剩余的元素,即:
f(1, m) = 0
根据递推关系,即可写出代码
代码
class Solution {
public:
int lastRemaining(int n, int m) {
int last = 0;
for (int i = 2; i <= n; ++i) {
last = (last + m) % i;
}
return last;
}
};