重点:头指针 first & 尾指针 cut
思路:
1、先创建第一个节点,让 first 指向该节点,并形成环状
2、后面每创建一个新节点,就把该节点加入到已有的环形链表中
遍历:
1、先用辅助指针指向 first 节点
2、然后通过 while 循环遍历,终止条件: cut.next = first
节点类:
// 节点类
class children{
int id;
children next;
public children(int id) {
this.id = id;
}
}
环形链表类:
class RingSingleLinkedList{
// 初始化,头节点,尾指针
children first = null;
public void add(int x) {
// 检查参数
if (x < 1) {
System.out.println("x 数值错误");
return;
}
// 创建尾指针
children last = null;
// 开始 add 节点
for (int i = 1; i <= x ; i++) {
// 循环一次,创建一个节点
children children = new children(i);
// 初始化头节点
if (i == 1) {
first = children;
// 此时环形链表中只有一个节点,该节点的 next 属性指向自身,形成环
first.next = first;
// last 指向 头节点
last = first;
} else {
// 使 last.next 指向新节点
last.next = children;
// 使新节点.next 指向 first
children.next = first;
// last 后移,指向最尾元素
last = children;
}
}
}
// 解决 约瑟夫 问题
// 解决 约瑟夫问题
/**
* 思路:
* 1. 需要一个辅助指针,事先指向环形链表的最后 ;
* 补充: 小孩报数前, 先让 first 和 temp 移动 k-1 次
* 2. 当小孩报数时, 让 first 和 temp 同时移动 (m - 1)下
* 3. 此时, 将 first 指向的小孩节点出圈,
* first = first.next;
* temp.next = first; (原先被 first 指向的节点因为没有引用, 就会被 gc 机制回收)
*
* @param start 第 k 个人开始报数
* @param m 报数报到 m 的节点弹出
* @param number 环中小孩的总数
*/
public void joseph(int start, int m, int numbers) {
// 检查参数
if (m < 1 || start > numbers || first == null) {
System.out.println("参数错误");
return;
}
// 创建辅助指针
children temp = first;
// 移动辅助指针,使指向 first 节点的前一个节点
for (int i = 1; i < numbers; i++) {
temp = temp.next;
}
// 此时 temp 指向 first 前一个节点,
// 报数前的准备工作,两指针同时前移 start-1 个单位
for (int i = 0; i < start-1; i++) {
first = first.next;
temp = temp.next;
}
// 开始报数
while (true) {
// 两指针同时移动 m-1 个单位,此时 first 指向的节点就是 需要被弹出的节点
for (int i = 0; i < m-1; i++) {
first = first.next;
temp = temp.next;
}
System.out.printf("编号为 %d 的节点被弹出\n", first.id);
// 后移 first
first = first.next;
// 让 temp 指向后移后的 first,此时,被弹出的节点因为没有引用指向,被 gc机制回收
temp.next = first;
// 终止循环的条件
if (temp == first) {
break;
}
}
System.out.printf("最后剩余的节点编号为 %d", temp.id);
}
// show
public void show() {
// 判断是否为空
if (first == null) {
System.out.println("空链表");
return;
}
// 辅助指针
children temp = first;
System.out.println("开始遍历:");
while (true) {
System.out.printf("编号 %d 弹出\n", temp.id);
if (temp.next == first) {
break;
}
temp = temp.next;
}
}
测试代码:
public class RingSingleLinkedListDome {
public static void main(String[] args) {
RingSingleLinkedList ringSingleLinkedList = new RingSingleLinkedList();
ringSingleLinkedList.add(5);
ringSingleLinkedList.joseph(1, 2, 5);
}
}
测试的结果为:
编号为 2 的节点被弹出
编号为 4 的节点被弹出
编号为 1 的节点被弹出
编号为 5 的节点被弹出
最后剩余的节点编号为 3