zoukankan      html  css  js  c++  java
  • 单向环形链表 —— 约瑟夫环问题

    一、单向环形链表介绍

      

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

      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
    ", 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出圈
    ", first.getNo());
    113             // 这时将 first 指向 的小孩节点出圈
    114             first = first.getNext();
    115             helper.setNext(first);
    116         }
    117 
    118         System.out.printf("最后留在圈中的小孩编号%d
    ", 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 }
  • 相关阅读:
    iOS开发常用的第三方框架
    回传值(代理、通知、block)
    给控制器添加工具栏(Swift语言)
    Swift语言 代码添加文本输入框 和 占位文本
    MD5加密运算
    Xcode7.0设置网络白名单
    base64加密解密文件
    关于iOS应用管理之九宫格的坐标计算以及与UIScrollView的结合
    3、Struts2中的参数传值
    2、Action的多种写法配置
  • 原文地址:https://www.cnblogs.com/niujifei/p/11582631.html
Copyright © 2011-2022 走看看