

 
 


 
 
 
 
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;
     }
 }



















