约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。又称“丢手绢问题”.)
N个人围成一圈,从第X个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。
例如N=5,X=1, M=2,被杀掉的顺序是:2 , 4 , 1 , 5 , 3
Java环形链表实现
1.模拟链表的 单个节点的数据结构
class Boy {
private int number; 小孩编号
private Boy next; 小孩下一位
public Boy(int number) {
this.number = number;}
public Boy getNext() {
return next;}
public void setNext(Boy next) {
this.next = next; }
}
2. 添加小孩并形成环
1 .定一个辅助 cur 指针 始终指向最新添加进来的小孩 2.当第一个小孩时,自己指向自己
3.当不是第一个时, 将 cur 的下一位指向新添加进来的 ,将新添加进来的 指向第一个形成环 , 将cur后移指向自己
public void add(int num) {
if (num < 1) {
System.out.println("数量太小!");
return;
}
Boy cur = null; //定一个辅助指针变量
for (int i = 1; i <= num; i++) {
if (i == 1) {
first = new Boy(1); //如果是第一个 就定义第一个小孩
first.setNext(first); //将自己指向自己
cur = first; //将当前指向first , 后面继续使用
} else {
Boy boy = new Boy(i); //不是第一个,根据编号创建
cur.setNext(boy); //将当前指针的下一位指向,新来的
boy.setNext(first); //将新来的指向第一个,形成环
cur = boy; //将当前指向新来的 , 后面继续使用
}}}
3.实现约瑟夫问题
1. 因为是单向的环形链表,所以定一个辅助指针 helper 始终指向 cur 后面的一位
2.先让 helper cur 同时移动 startNumber-1个 因为数数自己也算一个
3.开始剔除时,结束条件 : cur== helper
4.数数时 helper cur同时移动 countNumber - 1 个 后 cur就是需要出圈的小孩
cur=cur.getNext(); 让cur后移一位, 即下一位开始报数的人
helper.setNext(cur); 让helper指向 出圈的小孩的下一个, 即下一位开始报数的人,就是让当前first小孩出圈
5.first为第一个加入的小孩
1.第一循环将helper 放在first 后面去 2.for循环将first, helper 同时移动 startnumber-1个,即从第几个开始数
3.开始数数, 如果first==helper 说明到了最后一个结束循环 将first, helper 同时移动 countnumber-1个 ,first指向的为出圈的
4.将first后移,将helper 指向新的first,即将旧的first出圈
public void ShowJosephProblem(int startNumber, int countNumber, int BoyNumbers) {
this.add(BoyNumbers);
if (startNumber < 1 || startNumber > BoyNumbers) {
System.out.println("参数非法");
return;
}
Boy helper = first; //先让helper指向first 再循环使他指向最后一个
while (true) {
if (helper.getNext() == first) {
break;
}
helper = helper.getNext();
}
//让first 指向 第一个开始数数的小孩
for (int i = 0; i < startNumber - 1; i++) { //因为自己数算一个,所以是移动 countNumber-1个
helper = helper.getNext();
first = first.getNext();
}
//开始数数,
while (true){
if (first ==helper){ //因为 helper 跟在后面 当最后一个小孩时他们相等
break;
}
//让first,helper同时移动countNumber- 1个后 ,现在first 指向需要出圈的小孩,helper指向它的前一个
for (int i = 0; i < countNumber - 1; i++) {
first = first.getNext();
helper=helper.getNext();
}
System.out.printf("当前出圈小孩编号: %d \n", first.getNumber());
first = first.getNext(); //让first后移一位, 即下一位开始报数的人
helper.setNext(first); //让helper指向 出圈的小孩的下一个, 即下一位开始报数的人,就是让当前first小孩出圈
}
System.out.printf("最后小孩编号: %d", first.getNumber()); }}