一、单向环形链表介绍

  

二、单向环形链表应用场景

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

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

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

三、Josephu 问题

  1、约瑟夫的示意图

    

  2、Josephu 问题

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

  3、提示

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

  4、约瑟夫问题 — 创建环形链表的思路图解

     

  5、约瑟夫问题 — 小孩出圈的思路分析图

     

  6、代码实现

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

 

posted on 2019-09-25 09:57  格物致知_Tony  阅读(782)  评论(0编辑  收藏  举报