1、单向环形链表
-
构建一个单向的环形链表思路
- 先创建第一个节点,让first指向该节点,并形成环形
- 后面当每创建一个新的节点,就把该节点,加入到已有的环形链表中即可.
-
遍历环形链表
-
先让一个辅助指针(变量)curChild,指向first节点
-
然后通过一个while循环遍历该环形链表即可
-
2、约瑟夫(Josephus)问题
-
Josephus问题为:设编号为1,2,…n的n个人围坐一圈,约定编号为k (1<=k<=n)的人从1开始报数,数要到m的那个人出列,它的下一位又从1并始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
-
约定:n=10,即有10个人;k = 2,从第二个人开始报数;m=3,数3下
-
思路
- 需要创建一个辅助变量beforeFirst,事先应该指向环形链表的最后这个节点.
- 小孩报数前,先让first和beforeFirst移动 k - 1 次
- 当小孩报数时,让first和beforeFirst指针同时的移动 m - 1 次
- 这时就可以将first指向的小孩节点出圈
- first = first.next
- beforeFirst.next=first
- 原来上一个first指向的节点就没有任何引用,就会被回收
3、代码实现
public class Josephus {
public static void main(String[] args) {
//测试环形链表的构建和遍历
CircleSingleLinked circleSingleLinkedList = new CircleSingleLinked();
circleSingleLinkedList.addChild(10); //加入小孩结点
System.out.println("所有的小孩编号如下:");
circleSingleLinkedList.showChild();
System.out.println();
//测试一把小孩出圈
System.out.println("小孩的出圈情况如下:");
circleSingleLinkedList.countChild(2, 3, 10); //2->4->1->5->3
}
}
class CircleSingleLinked {
//创建一个first结点,当前没有编号
private Child first = new Child(-1);
//添加小孩及结点,构建成一个环形链表;childNum小孩个数
public void addChild(int childNum) {
if (childNum < 1) { //至少有一个小孩
System.out.println("至少有一个小孩");
return;
}
Child curChild = null; //辅助变量,帮助构建环形链表
//使用for来创建环形链表
for (int i = 1; i <= childNum; i++) {
//根据编号,创建小孩结点
Child child = new Child(i);
//如果是第一个小孩
if (i == 1) {
first = child;
first.next = first; //构成环
curChild = first; //让curChild指向第一个小孩
} else {
curChild.next = child;
child.next = first;
curChild = child;
}
}
}
//遍历当前环形链表
public void showChild() {
//判断链表是否为空
if (first.id == -1) {
System.out.println("没有小孩");
return;
}
Child curChild = first;
while (true) {
System.out.println("小孩的编号为" + curChild.id);
if (curChild.next == first) { //说明已经遍历完毕
break;
}
curChild = curChild.next;
}
}
//根据用户的输入,计算出小孩出圈的顺序
/**
* @param startId 表示从第几个小孩开始数数 即题中的k
* @param countNum 表示数几下,即题中的m
* @param childNum 表示最初有对少个小孩在圈中,即题中的n
*/
public void countChild(int startId, int countNum, int childNum) {
//先对数据进行校验
if (first.id == -1 || startId < 1 || startId > childNum) {
System.out.println("参数输入有误,请重新输入");
return;
}
Child helper = first;
//使 helper.next == first
while (helper.next != first) {
helper = helper.next;
}
//小孩报数前,先让first和helper移动startId-1次
for (int i = 0; i < startId - 1; i++) {
first = first.next;
helper = helper.next;
}
//当小孩报数时,让first和helper指针同时的移动 countNum - 1次,然后出圈
//这里是一个循环操作,直到圈中只有一个结点
while (helper != first) {
//让first和helper指针同时的移动countNum -1次,然后出圈
for (int j = 0; j < countNum - 1; j++) {
first = first.next;
helper = helper.next;
}
//这时first指向的结点,就是要出圈的小孩结点
System.out.println("出圈的小孩编号为" + first.id);
//这时将first指向的小孩出圈
first = first.next;
helper.next = first;
}
System.out.println("最后留在圈中的小孩编号为" + first.id);
}
}
//创建一个Child类,表示一个结点
class Child {
public int id; //编号
public Child next; //指向下一个结点,默认为null
public Child(int id) {
this.id = id;
}
}