约瑟夫问题--循环链表实现

约瑟夫问题--循环链表实现

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

单向环形链表实现

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

    1. 先创建第一个节点,让first指向该节点,并形成环形,即first.next=first
    2. 再每次创建一个新节点时就把该节点加入到已有的环形链表中即可
  • 遍历环形链表

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

    2. 然后通过一个while循环遍历该环形链表即可

      • newBoyNodePre.next=newBoyNode;
      • newBoyNode.next=first;
      • curBoy=newBoyNodePre.next;或者curBoy=newBoyNode;
        • curBoy.next=first;则遍历结束

      代码实现

      1. 创建指定个数的环形链表和展示环形链表
      package com.guodaxia.josepfu;
      /**
      * @ author Guo daXia
      * @ create 2022/11/16
      */
      public class Josepfu {
      public static void main(String[] args) {
      CircleSingleLinkedList list = new CircleSingleLinkedList();
      list.addBoy(5);
      list.showBoy();
      }
      }
      /**
      * 创建一个单向循环链表
      */
      class CircleSingleLinkedList{
      //创建一个first节点
      private Boy first = null;
      //添加小孩节点,构建成一个环形的链表
      public void addBoy(int nums){
      if (nums<1){
      System.out.println("nums不正确");
      return;
      }
      //辅助指针,帮助遍历节点
      Boy curBoy = null;
      for (int i = 1;i<=nums;i++){
      //根据编号,创建小孩节点
      Boy boy = new Boy(i);
      if (i==1){//如果是添加第一个小孩时,较为特殊
      first=boy;
      first.setNext(first);//boy的next为first,构成环。
      curBoy=first;//让curBoy指向第一个小孩
      }else {
      curBoy.setNext(boy);
      boy.setNext(first);
      curBoy=boy;
      }
      }
      }
      //遍历当前环形链表
      public void showBoy(){
      if (first==null){
      System.out.println("没有任何小孩~");
      return;
      }
      //因为first不能动,所以我们任然使用一个辅助指针进行遍历
      Boy curBoy = first; //从first第一个男孩开始遍历
      while (true){
      System.out.printf("小孩的编号%d\n",curBoy.getNo());
      if (curBoy.getNext()==first){break;}
      curBoy= curBoy.getNext();//使辅助指针后移,达成循环迭代条件
      }
      }
      }
      /**
      * 创建一个Boy类,表示一个节点
      */
      class Boy{
      private int no;//编号
      private Boy next;//指向下一个节点,默认位null
      public Boy(int no) {this.no = no;}
      public int getNo() {return no;}
      public Boy getNext() {return next;}
      public void setNo(int no) {this.no = no;}
      public void setNext(Boy next) {this.next = next;}
      }
      1. 实现逻辑,按用户指定从那个小孩开始报数,直到报到 几下数结束,并将最后一个报数小孩出圈。
      /**
      * 根据用户输入,计算出小孩出圈的顺序
      * @param startNo 表示从第几个小孩开始报数
      * @param countNum 表示报几下
      * @param nums 表示初始人数有多少
      */
      public void countBoy(int startNo,int countNum,int nums){
      //先对数据校验
      if (first==null||startNo<1||startNo>nums){
      System.out.println("无法玩!");
      return;
      }
      //创建一个辅助指针 帮助完成小孩出圈
      Boy helper = first;
      //1.先让helper指向环形链表的最后一个节点
      while (true){
      if (helper.getNext()==first){
      break;
      }
      helper=helper.getNext();
      }
      //2.小孩报数前,先把helper和first移动startNo-1次(startNo是从第几个小孩开始报数)
      for (int i=0;i<startNo-1;i++){
      first=first.getNext();
      helper=helper.getNext();
      }
      while (true){
      if (helper==first){//说明场上只剩一个小孩了
      break;
      }
      //3.当小孩开始报数时,让helper和first指针同时移动countNum-1次(countNum是报几下)
      for (int j=0;j<countNum-1;j++){
      first=first.getNext();
      helper=helper.getNext();
      }
      //4.这时就可以将first指针指向的小孩节点出圈,原来的first指向的节点就会被回收
      System.out.printf("编号为%d的小孩出圈\n",first.getNo());
      first=first.getNext();//first指针下移一位
      helper.setNext(first);//将helper.next指向first
      }
      //跳出循环,可以把最后一个小孩节点打印出来、
      System.out.printf("场上剩下的最后一个编号为%d \n",helper.getNo());
      }
      }
posted @   gdxstart  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示