对于 官方题解 进行了一定的修改和补充。
数学 + 递归
将问题建模为函数 f(n, m)
,函数的返回值为最终留下的元素的序号。
长度为 n
的序列会先删除第 m % n
个元素,然后剩下一个长度为 n - 1
的序列。假设对于剩下的 n - 1
个元素,最终会留下第 x
个元素,即 x = f(n - 1, m)
。也就是说,对于长度为 n
的序列,删除第 m % n
个元素后,继续向后数了 x
个元素,为最终剩下的元素。因此有 f(n, m) = (m % n + x) % n = (m + x) % n
。
递归计算 f(n, m), f(n - 1, m), f(n - 2, m), ...
直到递归的终点 f(1, m)
。当序列长度为 1 时,一定会留下唯一的那个元素,它的编号为 0。
class Solution {
int f(int n, int m) {
if (n == 1) {
return 0;
}
int x = f(n - 1, m);
return (m + x) % n;
}
public:
int lastRemaining(int n, int m) {
return f(n, m);
}
};
递归改写为迭代
从 f(1, m) = 0
开始迭代,直到 f(n, m)
。根据上文的 f(n, m) = (m + x) % n
,可以得到:
f(2, m) = (m + f(1, m)) % 2
f(3, m) = (m + f(2, m)) % 3
···
f(n, m) = (m + f(n - 1, m)) % n
i
取 [2, n]
,迭代公式为 f = (m + f) % i
。
class Solution {
public:
int lastRemaining(int n, int m) {
int f = 0;
for (int i = 2; i <= n; i++) {
f = (m + f) % i;
}
return f;
}
};
注意到有的题编号从 1
开始,直接在 return
的结果上加 1
就好了。
有的题有特殊用例,序列长度 n
为 0
,题中会提示这种情况返回 -1
。