著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,一开始要站在什么地方才能避免被处决?
设编号为1,2,…n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
这里使用的是环形单链表来解决这个问题。假设一共有5个人,从编号为1的人开始报数,数到2的那个人就要淘汰。如下图,先将5个人加入到环形链表中。
从1开始报数,标号2的报到2,所以淘汰还剩下1 3 4 5 淘汰2
然后从3号开始,重新报数,4号被淘汰。 剩下1 3 5 淘汰2 4
4号淘汰后,从5号开始报数,淘汰掉1号。剩下3 5 淘汰 2 4 1
再然后就从3号开始,淘汰掉3号。最后只剩下5号
所以最终出队的顺序为 2 4 1 3 5。
这里把成员变量都设为私有了,所以下面加上了set和get方法。也可以设置为公共,就不需要下面的set和get方法了。
class Person {
private int id;
private Person next;
public Person(int n) {
id = n;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Person getNext() {
return next;
}
public void setNext(Person next) {
this.next = next;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
'}';
}
}
设置一个first指针,指向开始节点。最开始里面是没有节点的,所以先为空。
class CircleSingleLinkedList {
private Person first = null;
}
添加节点的步骤如下图(将5号加入到链表中)
传参为参加游戏的总人数。
public void addPerson(int n) {
if (n < 1) {
System.out.println("添加人数要大于等于1人");
return;
}
//cur指向当前的节点,也就是最后加入的节点。
Person cur = null;
for (int i = 1; i <= n; i++) {
Person person = new Person(i);
if (i == 1) {
first = person;
first.setNext(first);
cur = person;
} else {
cur.setNext(person);
person.setNext(first);
cur = person;
}
}
}
public void show() {
if (first == null) {
System.out.println("该链表为空。。。");
return;
}
Person curPerson = first;
while (true) {
System.out.println(curPerson.getId());
if (curPerson.getNext() == first) {
break;
}
curPerson = curPerson.getNext();
}
}
如图,要将2号淘汰,只需要指向所示的两部,就没有指针指向2号,那么2号就会被回收机制所回收。
传参 startid为开始报数的位置,count为报到几后淘汰。n为总人数,用来校验参数的合理性。
public void run(int startid, int count, int n) {
if (first == null || startid < 1 || count > n) {
System.out.println("请给出合理的参数...");
return;
}
//设置一个辅助指针helper,使它始终指向first的前一个节点,因为first最后指向的节点就是要被淘汰的,单向链表,我们就需要用helper来淘汰此节点。
Person helper = first;
while (true) {
if (helper.getNext() == first) {
break;
}
helper = helper.getNext();
}
//使first指向开始报数的人,helper也指向对应的前一个人。
for (int i = 0; i < startid - 1; i++) {
first = first.getNext();
helper = helper.getNext();
}
while (true){
if (first == helper){
break;
}
for (int i =0;i<count -1 ;i++){
first = first.getNext();
helper = helper.getNext();
}
System.out.println("淘汰的为" + first.getId());
first = first.getNext();
helper.setNext(first);
}
System.out.println("最后剩下的为"+ first.getId());
}
public static void main(String[] args) {
CircleSingleLinkedList cs= new CircleSingleLinkedList();
cs.addPerson(5);
cs.show();
cs.run(1,2,5);
}