问题描述
0,1,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。
该题目来源于leetcode,点击进入
读题
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
解法一
比较直观的解法
public int lastRemaining(int n, int m) {
LinkedList<Integer> list = new LinkedList<>();
// 全部加进去
for (int i = 0; i < n; i++) {
list.add(i);
}
// 只要链表长度不为 1,就不断循环
while (list.size() != 1) {
for (int i = 0; i < m; i++) {
// 取出第一个元素并删除
Integer pre = list.pollFirst();
if (i != m - 1) {
// 如果不是第m个,就加到末尾
list.add(pre);
}
}
}
return list.pollFirst();
}
解法二
首先,我们设定n个数值中不断删除m位,最后得到的值为f(n,m)
第一次删掉的数字为(m-1)%n
记做x
那么剩下的n-1个数字就变成了(此处假设n大于2)
0 | 1 | … | x-1 | x+1 | … | n-1 |
---|
因为需要从被删掉的数字后面一位重新计数,我们把X+1放到首位,从新排列后的数字顺序为
原始数组 | 对应顺序位置 |
---|---|
x+1 | 0 |
x+2 | 1 |
… | … |
n-1 | n-x-2 |
0 | n-x-1 |
… | … |
x-1 | n-2 |
为什么0对应的位置是n-x-1?
我们第一步的时候删除了一个数字,可以理解成把这个链表分成了两部分,
第一部分的长度为x(因为是从0开始的),
那么第二部分的长度就为 n-x-1
现在把第一部分拼接到第二部分的后面,
那么第一分部的第一个数,也就是0,就变成了n-x-1+1
个数字,也就是n-x个数字,因为是从0开始的,所以对应的就是n-x-1
关键点:
假设对应的顺序位置为A,原始数值为B
那么顺序位置A与数值B的关系我们可以得出公式:
B = (A+x+1) mod n
由最初的定义可以得到:
n-1个数字中不断删除第m个,最后得到的值为f(n-1,m)
从我们上面的公式可以反推回它的值为(f(n-1,m)+x+1)%n
这个值也就是我们开始定义的f(n-1,m)
将x=(m-1)%n
代入简化可得:
f(n,m)=(f(n−1,m)+m) mod n,且f(1,m) = 0
代码实现
public static int lastRemaining(int n, int m) {
return n == 1 ? 0 : (lastRemaining(n-1,m) + m) % n;
}
或者
public int lastRemaining(int n, int m) {
int flag = 0;
for (int i = 2; i <= n; i++) {
flag = (flag + m) % i;
}
return flag;
}