• 链表(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
    ", 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 出圈
    ", first.getNo());
     97             first = first.getNext();
     98             helper.setNext(first);
     99         }
    100         System.out.printf("最后一个小孩的编号是first : %d,helper : %d
    ", 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

    以上

    朱子家训说:宜未雨而筹谋,勿临渴而掘井。 任何事情要到了跟前才想解决办法,那我们岂不很被动!
  • 相关阅读:
    Oracle中使用游标详解
    arc140 vp 记录
    CF1710D Recover theTree
    2022.8 做题记录
    21noip赛前20天 day10 简要题解
    2022.7.22 AGC028F&CF1463F&P7740
    arc141 vp 记录
    2022.7.25 AGC027F&AGC032F&AGC013F
    2022.7.21 AGC046D&P6790&AGC041F
    2022.7.20 AGC052D&P4338&AGC033E
  • 原文地址:https://www.cnblogs.com/jianyingjie/p/12169091.html
Copyright © 2020-2023  润新知