约瑟夫环问题

6.4 Josephu约瑟夫问题

josephu问题为:设编号为1、2、... n 的 n 个人围坐一圈,约定编号为 k (1 <= k <= n)的人从 1 开始报数,数到 m 的那个人出列,他的下一位又从 1 开始报数,数到 m 的那个人又出列,依此类推,直到所有人出列位置,由此产生一个出队编号的序列。

假设 n = 5; k = 1; m = 2; 那么出来的队列顺序就是 2 - 4 - 1 - 5 - 3(使用单项环形链表完成,也可以使用数组取模完成)

构建一个单向环形链表

  1. 先创建第一个节点,让 first 指向该节点, 并形成环形

  2. 后面当我们每创建一个新的节点,就把该节点,加入到已有的环形链表中即可

遍历环形链表

  1. 先让一个辅助指针(变量),指向 first 节点

  2. 然后通过一个 while循环遍历,该环形链表即可,即curBoy.next = first

根据用户的输入,生成一个小孩出圈的顺序

  1. 需要创建一个辅助指针helper,事先指向环形链表的最后一个节点

  2. 当小孩报数时,让first和helper同时移动 m - 1 次

  3. 这时就可以将first执行那个的小孩节点出圈

    first = first.next;

    helper.next = first;

    原来 first指向的节点就没有任何引用,就会被垃圾回收机机制回收

 

  1 package LinckedList;
  2   3 public class Josepfu {
  4     public static void main(String[] args) {
  5         // 测试,构建环形链表和遍历是否成功
  6         CircleSingleLinkedList c1 = new CircleSingleLinkedList();
  7         c1.addBoy(5); // 加入5个小孩的节点
  8         c1.showBoy();
  9  10         // 测试出圈
 11         c1.countBoy(1,2,5);
 12     }
 13 }
 14 // 创建一个环形的单向链表
 15 class CircleSingleLinkedList{
 16     // 创建一个first节点,当前没有编号
 17     private Boy first = new Boy(-1);
 18     // 添加小孩的节点,构建一个环形的链表
 19     public void addBoy(int num){
 20         // 对num做一个数据校验
 21         if (num < 1){
 22             System.out.println("nums的值不正确");
 23             return;
 24         }
 25         Boy curBoy = null; //  辅助指针,帮助构建环形链表
 26         // 使用一个for循环创建我们的环形链表
 27         for (int i = 1; i <= num; i++) {
 28             // 根据编号创建小孩节点
 29             Boy boy = new Boy(i);
 30             // 如果是第一个小孩
 31             if (i == 1){
 32                 first = boy;
 33                 first.setNext(first); // 构成环
 34                 curBoy = first; // 让curBoy指向第一个小孩
 35             } else {
 36                 curBoy.setNext(boy); // 先让curBoy指向的节点的下一个指向新节点
 37                 boy.setNext(first); // 在让新节点的指回第一个节点,构成环
 38                 curBoy = boy; // 最后令curBoy后移指向boy,等待下一个新的boy节点
 39             }
 40         }
 41     }
 42     // 遍历当前环形节点
 43     public void showBoy(){
 44         // 判断链表是否为空
 45         if (first == null){
 46             System.out.println("NULL");
 47             return;
 48         }
 49         // 因为first 不能动,因此我们使用辅助指针完成遍历
 50         Boy curBoy = first;
 51         while (true){
 52             System.out.printf("小孩的而编号为 %d\n",curBoy.getNo());
 53             if (curBoy.getNext() == first){
 54                 // 说明遍历完毕
 55                 break;
 56             }
 57             curBoy = curBoy.getNext(); // curBoy后移
 58         }
 59     }
 60  61     // 根据用户输入,计算出小孩出圈的顺序
 62  63     /**
 64      *
 65      * @param startNo 表示从第几个小孩开始报数
 66      * @param countNum 表示数几下
 67      * @param nums 表示最初有多少小孩在圈中
 68      */
 69     public void countBoy(int startNo, int countNum, int nums){
 70         // 先对数据进行校验
 71         if (first == null || startNo < 1 || startNo > nums){
 72             System.out.println("参数输入有误,请重新输入");
 73             return;
 74         }
 75         // 创建辅助指针,帮助出圈
 76         Boy helper = first;
 77         // 需求创建一个辅助指针helper, 事先应该指向环形链表的最后这个结点
 78         while (true){
 79             if (helper.getNext() == first){
 80                 break;
 81             }
 82             helper = helper.getNext();
 83         }
 84         // 报数前先让first和helper移动 startNo(k) - 1次
 85         for (int j = 0; j < startNo - 1; j++){
 86             first = first.getNext();
 87             helper = helper.getNext();
 88         }
 89         // 当小孩报数时,让first和helper同时移动 m - 1 次
 90         // 这里是一个循环的操作,直到圈中只有一个
 91         while (true){
 92             if (helper == first){
 93                 break;
 94             }
 95             // 让first和helper同时移动 countNum(m) - 1 次
 96             for (int j = 0; j < countNum - 1; j++){
 97                 first = first.getNext();
 98                 helper = helper.getNext();
 99             }
100             // 这时first指向的节点,就是要出圈的小孩的节点
101             System.out.printf("小孩%d出圈\n",first.getNo());
102             // 这时将first出圈
103             first = first.getNext();
104             helper.setNext(first);
105         }
106         System.out.printf("最后留在圈中的小孩编号时%d\n",first.getNo());
107     }
108 }
109 // 创建一个Boy类,表示一个节点
110 class Boy {
111     private int no; // 编号
112     private Boy next;
113 114     public Boy(int no){
115         this.no = no;
116     }
117 118     public int getNo() {
119         return no;
120     }
121 122     public void setNo(int no) {
123         this.no = no;
124     }
125 126     public Boy getNext() {
127         return next;
128     }
129 130     public void setNext(Boy next) {
131         this.next = next;
132     }
133 }

 



 

posted @ 2021-07-31 09:42  笔锋  阅读(124)  评论(0)    收藏  举报