package LinkedList_;
/*
* 单向环形链表应用场景——约瑟夫问题
* 设编号为1,2....n的n个人围成一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的人出列,
* 它的下一位又从1开始报数,数到m的人又出列.....以此类推,直到所有人出列,产生一个出队编号序列。
*
* 例如: n=5 => 总共有5个人
* k=1 => 从第1个人开始报数
* m=2 => 数2次
* 出队编号序列为:2-->4-->1-->5-->3
*
* 应用:用一个不带头结点的循环链表处理,构成一个有n个结点的循环链表,然后由k结点起从1开始计数,
* 计到m时,删除对应的结点,然后再从下一个结点从1开始计数,直到最后一个结点从链表中删除
*
* 构建单向环形链表思路:
* 1.先创建第一个结点,让first指向该结点,并形成环形
* 2.每创建一个结点,就把该结点加入到已有的环形链表中
*
* 遍历环形链表
* 1.先让一个辅助变量temp,指向first结点
* 2.通过while循环遍历该环形链表 当temp.next = first时结束
*
* 出队编号序列
* 1.创建一个辅助变量temp,事先应指向环形链表的最后一个结点
* 2.报数前,让first和temp移动 k-1 次
* 3.报数时,让first和temp同时移动 m-1 次
* 4.让first指向的结点出队
* first = first.next;
* temp.next = first;
* 出队结点没有任何引用,被回收
*
*/
public class RingLinkedList_ {
public static void main(String[] args) {
RingLinkedList ringLinkedList = new RingLinkedList();
ringLinkedList.addNode(5);//加入五个结点
ringLinkedList.showNode();//显示
ringLinkedList.countNode(1, 2, 5);//2-->4-->1-->5-->3
System.out.println("======================================");
RingLinkedList ringLinkedList2 = new RingLinkedList();
ringLinkedList2.addNode(38);
ringLinkedList2.countNode(2, 4, 38);
}
}
//创建环形单向链表
class RingLinkedList{
//创建first结点
public Node first = null;
//添加结点
public void addNode(int nums) {
//校验
if (nums < 1) {
System.out.println("nums值不正确");
return;
}
Node temp = null;//辅助变量,帮助遍历
//使用for循环创建环形链表
for (int i = 1; i <= nums; i++) {
//根据编号创建结点
Node node = new Node(i);
if (i == 1) {//先创建第一个结点,让first指向该结点,并形成环形
first = node;
first.next = first;//构成环形
temp = first;//让temp指向第一个编号
}else {
temp.next = node;
node.next = first;
temp = node;
}
}
}
//遍历(显示)
public void showNode() {
//判断是否为空
if (first == null) {
System.out.println("链表为空,没有结点");
return;
}
//因为first不能动,仍使用辅助变量temp完成遍历
Node temp = first;
while(true) {
System.out.printf("编号%d\n",temp.no);
if (temp.next == first) {//遍历完毕
break;
}
temp = temp.next;
}
}
//出队序列
//startNo表示k,从第几个开始数
//countNum表示m,数几次
//nums表示n,总共几人
public void countNode(int startNo,int countNum,int nums) {
//校验
if (first == null || startNo < 1 || startNo > nums) {
System.out.println("输入有误,请重新输入");
return;
}
//1.创建一个辅助变量temp,事先应指向环形链表的最后一个结点
Node temp = first;
while(true) {
if (temp.next == first) {//temp指向了最后结点
break;
}
temp = temp.next;
}
//2.报数前,让first和temp移动 k-1 次
for(int j = 0;j < startNo - 1;j++) {
first = first.next;
temp = temp.next;
}
//3.报数时,让first和temp同时移动 m-1 次
//循环直到只有一个结点
while(true) {
if (temp == first ) {//只有一个结点
break;
}
for(int j = 0;j < countNum - 1;j++) {
first = first.next;
temp = temp.next;
}//这时first指向的结点就是要出队的结点
System.out.printf("出队编号:%d\n",first.no);
//4.让first指向的结点出队
first = first.next;
temp.next = first;
}
System.out.printf("最后的编号为:%d\n",first.no);
}
}
//创建Node类,表示结点
class Node{
public int no;//编号
public Node next;//指向下一个结点,默认为null
public Node(int no) {
this.no = no;
}
}