题目
n个数字形成1个圈,数字从0开始。每次从这个圈中删除第m个数字(当前数字为第1个数字),求出这个圈中剩下的最后一个数字;
实现
方法1 单循环链表
思路:将n个节点形成一个不带头节点的单循环链表,设置一个计数器count(初始化为1)统计第几个数字;如果count=m,则删除当前节点,count=1,当前节点前移;否则继续向前遍历,count++;
代码实现:
public class Num18 {
public static void main(String[] args) {
Node_18 first=cycleList(17);
josephusList(first,17,5);
}
public static void josephusList(Node_18 first,int n,int m){
if(n<1 || m<1){
return;
}
if(m==1){
//the end is the result
}
if(n==1){
System.out.println(first.data);
}else{
Node_18 pre=first;
Node_18 p=first.next;
int count=2;
while(pre!=p){
if(count==m){
System.out.println(p.data);
pre.next=p.next;
p=pre.next;
count=1;
}else{
count++;
pre=p;
p=p.next;
}
}
System.out.println(p.data);
}
}
/*
* create the cycleList to test
*/
private static Node_18 cycleList(int n){
Node_18 first=new Node_18(0);
Node_18 r=first;
for(int i=1;i<n;i++){
Node_18 node=new Node_18(i);
r.next=node;
r=node;
r.next=first;
}
return first;
}
}
class Node_18{
int data;
Node_18 next;
public Node_18(int data){
this.data=data;
this.next=null;
}
}
方法2 分析规律
分析规律,找出一个递归式。
最初的n个数字(0,1,…,n-1),最后剩下的数字是关于n和m的方程f(n,m);这n个数字中,第一个被删除的数字为(m-1)%n=k,剩下的数字为(0,1,2,…,k-1,k+1,…,n-1)===>(k+1,…,n-1,0,1,…,k-1),该序列最后剩下的数字也是关于n-1和m的方程:f(n-1,m)===>f(n,m)=f`(-1,m),即最后剩下的数字相同;
删除一个数字后,将剩下的序列重新进行映射,映射后的序列仍是从0开始并且连续的数字,这样第一个被删除的数字是仍是(m-1)%m
k+1 0
k+2 1
…..
n-1 n-k-2
0 n-k-1
…
k-1 n-2
进行映射,设映射函数p,p(x)=(x-k-1)%n;
逆映射p`(x)=(x+k+1)%n
===>f(n-1,m)=p[f(n-1,m)]=[f(n-1,m)+k+1]%n将k替换
==>f(n,m)=[f(n-1,m)+m]%n n>1
f(n,m)=0 n=1
代码实现:
递归实现:
public static int LastRemaining(int n ,int m){
if(n==1){
return 0;
}
return (LastRemaining(n-1,m)+m)%n;
}
非递归实现:
public static int LastRemaining_2(int n,int m){
int last=0;
for(int i=2;i<=n;i++){
last=(last+m)%i;
}
return last;
}