约瑟夫问题是一个经典的问题,描述如下:
有n个人围成一圈,从第一个人开始报数,数到m的人出圈,剩下的人继续从1开始报数,数到m的人再出圈,如此循环,直到只剩下一个人。
1、构建一个单向的环形链表思路
(1). 先创建第一个节点, 让 first 指向该节点,并形成环形
(2). 后面当我们每创建一个新的节点,就把该节点,加入到已有的环形链表中即可
2、遍历环形链表
(1). 先让一个辅助指针(变量)temp,指向first节点
(2). 然后通过一个while循环遍历 该环形链表即可temp.next == first 结束
代码实现:
public class SinglyCircularLinkedNode {
public int num;
public SinglyCircularLinkedNode next;
public SinglyCircularLinkedNode(int num) {
this.num = num;
}
}
class SinglyCircularLinkedList{
//约瑟夫问题:丢手绢问题,围成一圈,数数,数到谁谁出列,直到最后剩余一个人
//无头节点,初始化第一个节点
public SinglyCircularLinkedNode first = null;
/**
* 按照节点总数添加
* @param num
*/
public void createList(int num){
//数据验证
if (num < 1){
System.out.println("输入人数不合法");
return;
}
//建立一个辅助节点
SinglyCircularLinkedNode temp = null;
for (int i =1; i <= num; i++){
SinglyCircularLinkedNode singlyCircularLinkedNode = new SinglyCircularLinkedNode(i);
//只有一个节点的情况下,自己指向自己,形成一个循环
if (i == 1){
first = singlyCircularLinkedNode;
first.next = first;
temp = first;
}else {
temp.next = singlyCircularLinkedNode;
singlyCircularLinkedNode.next = first;
temp = singlyCircularLinkedNode;
}
}
}
/**
* 遍历链表
*/
public void showAll(){
//有效性验证
if (first == null){
System.out.println("链表内无节点");
return;
}
//辅助节点
SinglyCircularLinkedNode temp = first;
while (true){
//最后一个节点
if (temp.next == first){
System.out.print(temp.num);
System.out.println(" 遍历结束");
break;
}
System.out.print(temp.num + " => ");
temp = temp.next;
}
}
/**
* 开始丢手绢
*
* @param startNo 表示从第几个人开始数数
* @param countNum 表示每次报数几
* @param nums 表示最初圈内的总人数
*/
public void outNode(int startNo, int countNum, int nums){
// 数据校验
if (first == null || startNo < 1 || startNo > nums) {
System.out.println("参数输入有误, 请重新输入");
return;
}
// 创建辅助指针
SinglyCircularLinkedNode helper = first;// 初始化自己套自己,因为圈内目前就一个
// 需求创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点
while (true) {
if (helper.next == first) {
// 已经到最后一个
break;
}
// 后移
helper = helper.next;
}
//从第几个人开始报数
for (int j = 0; j < startNo - 1; j++) {
first = first.next;
helper = helper.next;
}
//报数
while (true) {
if (helper == first) {
// 说明圈中只有一个节点
break;
}
// 让 first 和 helper 指针同时 的移动 countNum - 1
for (int j = 0; j < countNum - 1; j++) {
first = first.next;
helper = helper.next;
}
// 这时first指向的节点,就是要出圈的节点
System.out.printf("节点%d出圈\n", first.num);
// 这时将first指向的节点出圈
helper.next = first.next;
first = first.next;
}
System.out.printf("最后留在圈中的节点编号%d \n", first.num);
}
}
测试:
public class SinglyCircularLinkedList_Test {
public static void main(String[] args) {
//初始化链表
SinglyCircularLinkedList singlyCircularLinkedList = new SinglyCircularLinkedList();
singlyCircularLinkedList.createList(5);
singlyCircularLinkedList.showAll();
singlyCircularLinkedList.outNode(1,2,5);
}
}
输出:
1 => 2 => 3 => 4 => 5 遍历结束
节点2出圈
节点4出圈
节点1出圈
节点5出圈
最后留在圈中的节点编号3
Process finished with exit code 0