题目
题目:
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 <= 105
1 <= m <= 106
补充:
这种问题可以说是约瑟夫环问题
思路-递归
补充一个定理:
(a+b)%c=((a%c)+(b%c))%c
a%c=(a%c)%c
首先,长度为 n 的序列会先删除第 m % n 个元素,然后剩下一个长度为 n - 1 的序列。那么,我们可以递归地求解 f(n - 1, m),就可以知道对于剩下的 n - 1 个元素,最终会留下第几个元素,我们设答案为 x = f(n - 1, m)
暗含在其中的数学逻辑是:
-
因为要从0数m个数,那最后肯定落到了下标为m-1的数身上了,但这个下标可能超过我们有的最大下标(n-1)了。所以攒满n个就归零接着数,逢n归零,所以要模n。所以有n个数的时候,我们划掉了下标为(m-1)%n的数字
-
往后数x+1,它下标肯定变成了(m-1)%n +x+1,和第一步的想法一样,你肯定还是得取模,所以答案为[(m-1)%n+x+1]%n
所以f(n,m)= [(m-1)%n+x+1]%n = (m+x)%n
,而且x = f(n - 1, m);
使用递归的思想
class Solution {
public int lastRemaining(int n, int m) {
if (n == 1) {
return 0;
}
return (m + lastRemaining(n - 1, m)) % n;
}
}
思路-迭代
递归和迭代的思想差不多
class Solution {
public int lastRemaining(int n, int m) {
int f = 0;
for (int i = 2; i != n + 1; ++i) {
f = (m + f) % i;
}
return f;
}
}
思路-单循环链表
通过循环链表来进行运算
这个算法是网上的算法看来的,还未结合leetcode进行整合
但思路都大同小异,函数名不一样而已
//运用循环单链表的方式实现
private void josephusList(Node first, int n, int m){
if(n < 1 || m < 1)
return;
if(n==1){
System.out.print(first.data+" ");
}
else{
Node pre = first; //当前节点的前驱
Node p = first.next; //当前节点
int count = 2;
while(p!=pre){
if(count==m){
System.out.print(p.data+" ");
Node r = p.next; //删除当前节点
pre.next = r;
p=r;
count = 1;
}
else{
pre = p;
p = p.next;
count++;
}
}
System.out.println("\n最后一个删除的元素:"+p.data);
}
}