链表(Linked List):单向环形链表

单向环形链表介绍

  • Josephu(约瑟夫、约瑟夫环)问题为

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

  • 提示

   用一个不带头节点的环形链表来处理 Josephu 问题:先构成一个有n个节点的单循环链表,然后由k节点起从1开始计数,记到m时,对应节点从链表中删除,然后再从被删除节点又从1开始计数,直到最后一个节点从链表中删除算法结束。

  • Josephu 问题的示意图

  • 约瑟夫问题-创建环形链表的思路图解

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

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

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

    遍历环形链表

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

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

  • 约瑟夫问题-小孩出圈的思路分析图

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

    n = 5 , 即有5个人

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

    m = 2, 数2下

 

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

    补充: 小孩报数前,先让 first 和 helper 移动 k - 1次

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

    3. 这时就可以将first 指向的小孩节点 出圈

    first = first .next

    helper.next = first

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

 

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

  • 约瑟夫问题的代码实现

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

以上

posted @ 2020-01-08 22:38  指尖上的生产者  阅读(766)  评论(0编辑  收藏  举报