小言_互联网的博客

单向环形链表和约瑟夫问题

368人阅读  评论(0)

单向环形链表和约瑟夫问题

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

n = 5 , 即有5个人

k = 1, 从第一个人开始报数

m = 2, 数2下

构建一个单向的环形链表思路

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

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

遍历环形链表

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

  2. 然后通过一个while循环遍历该环形链表即可

  3. curBoy.next==first结束

代码实现


  
  1. // 创建一个环形的单向链表
  2. class CircleSingleLinkedList {
  3.     // 创建一个first节点, 当前没有编号
  4.     private Boy first = new Boy(- 1);
  5.     // 添加小孩节点,构建一个环形的链表
  6.     public void addBoy (int nums) {
  7.         // nums 做一个数据校验
  8.         if (nums < 1) {
  9.            System.out.println( "nums的值不正确");
  10.             return;
  11.       }
  12.         Boy curBoy = null; // 辅助指针,帮助构建环形链表
  13.         // 使用for来创建我们的环形链表
  14.         for ( int i = 1; i <= nums; i++) {
  15.             // 根据编号,创建小孩节点
  16.             Boy boy = new Boy(i);
  17.             // 如果是第一个小孩
  18.             if (i== 1){
  19.                first=boy;
  20.                first.setNext(first); // 构成一个环状
  21.                curBoy=first; // 让curBoy指向第一个小孩
  22.           } else {
  23.                curBoy.setNext(boy);
  24.                boy.setNext(first);
  25.                curBoy=boy;
  26.           }
  27.       }
  28.   }
  29.     // 遍历当前环形链表
  30.     public void show (){
  31.         // 判断链表是否为空
  32.         if (first== null){
  33.            System.out.println( "没有任何小孩");
  34.             return;
  35.       }
  36.         // 因为first不能动,因此我们仍然使用一个辅助指针完成遍历
  37.         Boy curBoy = first;
  38.         while ( true){
  39.            System.out.println( "小孩的编号是:"+curBoy.getNo());
  40.             if (curBoy.getNext()==first){ // 说明已经遍历完毕
  41.                 break;
  42.           }
  43.            curBoy=curBoy.getNext(); // curBoy后移
  44.       }
  45.   }
  46. }
  47. // 创建一个Boy类,表示一个节点
  48. class Boy {
  49.     private int no; // 编号
  50.     private Boy next; // 指向下一个节点,默认是null
  51.     public Boy (int no) {
  52.         this.no = no;
  53.   }
  54.     public int getNo () {
  55.         return no;
  56.   }
  57.     public void setNo (int no) {
  58.         this.no = no;
  59.   }
  60.     public Boy getNext () {
  61.         return next;
  62.   }
  63.     public void setNext (Boy next) {
  64.         this.next = next;
  65.   }
  66. }

对以上方法测试


  
  1. public static void main (String[] args) {
  2.     //测试
  3.     CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
  4.    circleSingleLinkedList.addBoy( 25); // 加入5个小孩节点
  5.    circleSingleLinkedList.show();
  6. }

关于出圈问题

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

n = 5 , 即有5个人

k = 1, 从第一个人开始报数

m = 2, 数2下

  1. 需求创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点.

  2. 小孩报数前,先让 first 和 helper 移动 k - 1次

  3. 当小孩报数时,让first 和 helper 指针同时 的移动 m - 1 次

  4. 这时就可以将first 指向的小孩节点 出圈first = first .next helper.next = first 原来first 指向的节点就没有任何引用,就会被回收

  5. 出圈的顺序2->4->1->5->3

代码实现


  
  1. // 根据用户输入,计算出小孩的出圈顺序
  2. /**
  3. * @param startNo 表示从第几个小孩开始数
  4. * @param countNum 表示数几下
  5. * @param nums     表示最初有多少小孩在圈中
  6. */
  7. public void countBoy (int startNo, int countNum, int nums) {
  8.   // 先对数据进行校验
  9.   if (first == null || startNo < 1 || startNo > nums) {
  10.       System.out.println( "参数输入有误");
  11.       return;
  12.   }
  13.   // 这里做法是先将helper指向first,然后通过遍历 将helper指向最后一个节点
  14.   // 创建辅助指针,帮助小孩完成出圈
  15.   Boy helper = first; //指向环形单链表的最后一个节点
  16.   //创建一个辅助变量helper,事先应该指向环形链表的最后这个节点
  17.   while ( true) {
  18.       if (helper.getNext() == first) { // 说明helper指向最后小孩节点
  19.           break;
  20.       }
  21.       helper = helper.getNext();
  22.   }
  23.   // 寻找起始位置,把first定义为起始位置 移动k-1次
  24.   for ( int i = 0; i < startNo - 1; i++) {
  25.       first = first.getNext();
  26.       helper = helper.getNext();
  27.   }
  28.   // 当小孩报数时,让first和helper指针同时移动m-1次 然后出圈
  29.   // 这里是一个循环操作,知道圈中只有一个节点
  30.   while ( true) {
  31.       if (helper == first) { // 说明圈中只有一个节点
  32.           break;
  33.       }
  34.       // 让first和helper指针同时移动countNum-1次
  35.       for ( int i = 0; i < countNum - 1; i++) {
  36.           first = first.getNext();
  37.           helper = helper.getNext();
  38.       }
  39.       // 这时first指向的节点就是要出圈的节点
  40.       System.out.println( "小孩" + first.getNo() + "出圈");
  41.       // 这时将first指向的小孩节点出圈
  42.       first = first.getNext();
  43.       helper.setNext(first);
  44.   }
  45.   System.out.println( "最后留在圈中的小孩编号是:" + first.getNo());
  46. }
  47. // 这里在main方法里执行
  48. // 测试小孩出圈是否正确
  49. circleSingleLinkedList.countBoy( 1, 2, 5);

转载:https://blog.csdn.net/weixin_51971817/article/details/128756719
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场