约瑟夫问题:
据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。
猴子选大王问题:
100 只猴子坐成一个圈,从1开始报数,报到第14的那只猴子退出圈外,并重新开始计数。依次循环下去,直到圈中只剩下一一只猴子,就是大王。这个问题其实是一个特例,将其中的100 和14 换成变量n和m,就是约瑟夫环问题。
这是一类相同的问题。
代码如下:
import java.util.Iterator;
import java.util.LinkedList;
public class Joseph {
public static void main(String[] args){
//创建一个循环链表
LinkedList<Integer> linkedList=new LinkedList<>();
int number,cnt; //定义两个变量,用来计数
int target=10; //报出编号为target的人退出
//将数的编号放入链表中
for(number=1;number<=100;++number){
linkedList.addLast(number);
}
cnt=100;
number=0;
Iterator it=linkedList.iterator();
//循环删除退出的,知道只剩一个
while (cnt>1){
if(it.hasNext()){
it.next();
number++;
}else {
it=linkedList.iterator(); //到尾部时,将其重新置回到链表头部
}
//删除符合要求的
if(number==target){
number=0;
it.remove();
cnt--;
}
}
System.out.println("最终编号为:"+linkedList.element());
}
}
这里用链表(LinkedList)来解决这个问题。将每个人的编号存入到链表的一个节点中, 将这些点组成个链表。
为了让它围成一个圈,编程的时候需要稍微处理一下,用一个迭代器示每次到达链 表尾部的时候,又重新回到链表的头部来。
可以用一个计数器number模拟报数,等于14时,就将指向的节点删除,表示此人要退出圈外,另外,还要一个计数器cnt记录已经删除的节点数,初值为100,当它为1时表示已经选出最终数。