• 单循环链表之约瑟夫环详解


    约瑟夫环

      先来看一个有关约瑟夫环的小故事(没兴趣的直接跳过)

    据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。(这里我们将所有人杀死)

    画了一个约瑟夫环的示例图解:

        理解起来比较容易,但是代码部分有细节需要考虑(当first指向的结点出列后,具体见下面代码分析)

      首先2号出列

      

      接着第n+2个点到,也就是4号小孩出列

      

      以此类推,出列1号小孩

      

       最终得到出列小孩依次为 2、4、1、5、3

    代码部分:

      首先构建约瑟夫环(即循环单链表)

     1 private Boy cur=null;//这个辅助引用,代替头结点来完成循环链表的构建
     2 private Boy first=null;//头结点
     3 public void addBoy(int nums) {//传入多少人数
     4         if (nums < 2) {
     5             System.out.println("人数不得小于2人");
     6             return;
     7         }
     8         for (int i = 1; i <= nums; i++) {//遍历创建小孩结点
     9             Boy boy = new Boy(i);
    10             if (i == 1) {
    11                 first = boy;//将第一个结点赋给first
    12                 first.setNext(first);//形成一个环
    13                 cur = boy;//代替头结点完成接下来的链表构建
    14             }
    15             cur.setNext(boy);//连接新结点
    16             boy.setNext(first);//形成环
    17             cur = cur.getNext();//准备连接下一个结点
    18         }
    19         cur = cur.getNext();//让cur回到头结点处,以便后序操作
    20     }

      链表已经构建完成,接下来实现约瑟夫环(思路如下图):

       约瑟夫环核心代码:

     1     /*
     2      * start:从第几个小孩开始报数
     3      * m:报数从first为1开始累计m人,则第m人出队,然后继续循环
     4      * nums:一共nums个小孩
     5      * 
     6      * 比如有编号为1,2,3,4,5个小孩,start为1,m为2. 则出队顺序为:2,4,1,5,3
     7      */
     8     public void countBoy(int start, int m, int nums) {
     9         addBoy(nums);
    10 
    11         Boy helper = first;
    12 
    13         while (true) {
    14             if (helper.getNext() == first)
    15                 break;
    16             helper = helper.getNext();
    17         }
    18         // 从start开始报数
    19         for (int i = 1; i < start; i++) {//重置头结点至start处
    20             first = first.getNext();
    21             helper = helper.getNext();
    22         }
    23         while (true) {
    24             if (helper == first) {
    25                 break;
    26             }
    27             System.out.println("出队编号为" + first.getNo());
    28             first = first.getNext();//重置头结点为出列结点的下一个结点
    29             helper.setNext(first);//之前first的后一个结点helper不再指向出列的结点,指向头结点
    30             for (int i = 1; i < m; i++) {//此处要注意上面已经喊过一次“到”,这里少一次即可
    31                 helper = helper.getNext();
    32                 first = first.getNext();
    33             }
    34         }
    35         System.out.println("出队编号为" + first.getNo());
    36 
    37     }
  • 相关阅读:
    ubuntu mysql 的安装与简单操作
    JavaScript中的返回函数的函数
    JavaScript中的作用域
    JavaScript中的面向对象【一】
    JavaScript中的函数是数据
    JavaScript中的面向对象【二】
    JavaScript中的函数重写自己
    JavaScript中的匿名函数、回调函数、自调用函数
    JavaScript中的闭包
    mysql添加索引
  • 原文地址:https://www.cnblogs.com/taichiman/p/12843744.html
Copyright © 2020-2023  润新知