约瑟夫环问题
约瑟夫环(单向环形链表)
准备结点类
class Boy {
private int no;
private Boy next; //指向下一个结点
}
准备链表类
class CircleSingleLinkedList {
private Boy first = null;
}
构建一个约瑟夫环
思路:
1. 创建第一个结点,让first,curBoy指向该结点,并形成环形
2. 后面每创建一个新的结点,就把该结点,加入到已有的环形链表中
注意:
第一个结点创建完后curBoy指向第一个结点并且不发生移动,并且该结点指向自己,形成环状
后面创建的每一个结点都先让curBoy的next指向该结点,然后该结点next指向first
然后向后移动curBoy指针,curBoy可以理解为一个指向链尾的指针
public void addBoy(int nums) {
if (nums < 1) {
System.out.println("nums值不正确");
return;
}
Boy curBoy = null;
for (int i = 1; i <= nums; i++) {
Boy boy = new Boy(i);
if (i == 1) {
first = boy;
boy.setNext(first);
curBoy = first;
} else {
curBoy.setNext(boy);
boy.setNext(first);
curBoy = boy;
}
}
}
打印约瑟夫环
思路:
指针首先指向first,然后循环向后移动,当指针的下一个结点为first时,说明环已经遍历完成,结束循环
public void showBoys() {
//判断链表是否为空
if (first == null) {
System.out.println("没有任何小孩!");
return;
}
Boy curBoy = first;
while (true) {
System.out.printf("Boy %d\n", curBoy.getNo());
if (curBoy.getNext() == first) break;
curBoy = curBoy.getNext();
}
}
小孩出圈
问题描述:
有一圈小孩围坐在一起,每个小孩都按顺序标号,从指定标号小孩开始报数,报到n,这个小孩就出圈,直至圈中只剩一个小孩,求出圈序列
思路:
因为是单向链表,所以删除待删结点还是需要获取到前一个结点
默认待删结点是第一个结点,first指向它,然后创建一个helper用于指向前一个结点
定位开始报数的小孩标号startNo, first 和 helper 需要移动 startNo - 1 次
开始报数:报数到n,first 和 helper 需要移动 n -1 次
找到出圈结点,先打印结点序号,然后 first 后移一位,即指向下一次第一个报数的小孩
然后helper的next指向first,因为这是个重复的过程,所以当圈中只剩一个结点时,循环结束 ,即 first = helper
/**
* @param startNo 表示从第几个小孩开始数数
* @param countNum 表示数几下
* @param nums 表示最初有多少个小孩在圈中
*/
public void countBoy(int startNo, int countNum, int nums) {
//先对数据进行校验
if (first == null || startNo < 1 || startNo > nums) {
System.out.println("参数输入有误,请重新输入");
return;
}
//创建一个辅助结点,指向待删结点的前一个结点
//刚开始指向第一个结点first的前一个结点
Boy helper = first;
while (helper.getNext() != first) helper = helper.getNext();
//小孩报数前,先让 first 和 helper 移动 k-1 次,first指向第一个报数的小孩
//假如k=1,则不需要移动,first默认指向第一个,k=2,则需要移动一次
for (int i = 0; i < startNo-1; i++) {
first = first.getNext();
helper = helper.getNext();
}
//小孩报数时,报数到countNum,first 和 helper 需要移动 countNum-1 次
//假如countNum报数到1,则first不需要移动,countNum=2,则需要移动 1 次
//这里是一个循环的操作,直到圈中只有一个结点,每次小孩出圈,都要重新从1开始报数
while(first!=helper){
for (int i = 0; i < countNum-1; i++) {
first = first.getNext();
helper = helper.getNext();
}
//此时first指向出圈小朋友,helper指向前一个小朋友
System.out.printf("%d ",first.getNo());
first = first.getNext();
helper.setNext(first);
}
System.out.printf("%d\n",first.getNo());
}