描述:
n个人围成一个大圈,编号为0~n-1。然后,随机指定一个数m,从编号为0的人开始顺时针报数。每次喊到m-1的人要出列,接下来的人继续从0报数,最后剩下的人编号是多少?
示例1
输入: 5,3
返回值:3
思路:
利用约瑟夫问题的递推公式 f(n,m) =(f(n-1,m) + m) % n
公式递推:
令f(n,m)表示最后一个人的下标。
1.假设有n个人,报数m, 从0 开始报数,易知出圈的人下标为 (m-1)%n。
2.m-1 出圈后,我们对剩余人重新编号 即 原下标为m的人下标变为0,原下标为m+1的人 下标变为1 …以此编号。 那么重新编号之后,那么最后一个人的下标为f(n-1,m)
关键问题: 在重新编号之后的f(n-1,m) 与 重新编号之前的f(n,m)有什么关系?
假设(m-1)%n记为k
第k个人出列,此时
旧编号 k+1, k+2,k+3,…n-1 , 0, 1, …k-1
新编号 0 ,1 ,2,…n-k-2, .n-k-1, .n-k,…n-2
由此可得 新编号 = (旧编号 - (k+1))%n
所以 旧编号 = (新编号 + (k+1))%n
带入k = (m-1)%n
可知旧编号 = (新编号+m)%n
最终可得出 f(n,m) =(f(n-1,m) + m) % n
递归写法复杂度:
时间复杂度: O(N)
空间复杂度: O(N)
通过迭代写法可以简化空间复杂度为 O(1)
迭代代码,自底向上
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if (n <= 0) {
return -1;
}
int res = 0;
for (int i = 2; i<=n; i++) {
res = (res + m) % i;
}
return res;
}
}