一文让你秒懂“约瑟夫环问题”

先赞后看,养成习惯!

在这里插入图片描述

最近闲来无事,翻看了一下之前面试后做的记录,发现当时的自己真是“菜的一批”啊(虽说现在也挺菜,但是多少还是有那么一点点进步的)。追忆一下当年有点跑题了哈哈哈,今天是想跟大家分享一下当年遇到的一道面试题:“约瑟夫环问题”。

多得不说,少得不唠,下面开始今天的分享。

在这里插入图片描述

什么是约瑟夫环问题

大家知道什么是约瑟夫环吗?

各位读者是不是心里想着:“知道谁还看你在这废话。”然后悄悄地攥紧了拳头?别,各位大哥大姐千万别激动,给小弟点时间,让小弟为你排忧解难。

约瑟夫问题为:设编号为1,2,……n得n个人围坐一圈,约定编号为k(k大于等于1并且小于等于n)的人从1开始报数,数到m的那个人出列。它的下一位继续从1开始报数,数到m的人出列,以此类推,直到所有人都出列为止。

这就是著名的“约瑟夫环”问题,是不是还挺有趣的感觉(有趣个鬼不为了面试、加薪谁搞这东西)。

在这里插入图片描述

动手实现

我们可以通过单向环形链表来完成约瑟夫环问题。

第一步:构建单向环形链表

实现思路:让单向链表首尾相连从而实现环形链表(对单向链表不太了解的可以翻看一下之前的文章不再赘述)。

单向环形链表节点类:

public class Node {
  /**
   * 编号
   */
  public int no;

  /**
   * 指针域(指向下一个节点)
   */
  public Node next;

  /**
   * 构造方法
   * 
   * @param no
   */
  public Node(int no) {
    this.no = no;
  }
}

初始化单向环形链表类:

public class CircleSingleLinkedList {

  // 创建一个first节点
  private Node first;

}

创建单向环形链表的方法:

/**
 * 
 * @param nums 环形链表中有几个节点
 */
public void initCircleSingleLinkedList(int nums) {
    if (nums < 1) {
      throw new RuntimeException("入参有误!");
    }
    // 定义辅助变量
    Node cur = null;
    // 创建环形链表
    for (int i = 1; i <= nums; i++) {
      // 创建节点
      Node node = new Node(i);
      if (i == 1) {
        // 初始化第一个节点。
        first = node;
        // 将该节点的指针域指向自己,构成环形。
        first.next = first;
        // 初始化辅助变量。
        cur = first;
      } else {
        // 将辅助变量的指针域指向新节点。
        cur.next = node;
        // 链表首尾相连
        node.next = first;
        // 后移辅助变量
        cur = node;
      }
    }
}

以上代码就可以轻松地构建出一个单向环形链表,还是挺简单的吧!

第二步:编写出列方法

实现思路:

问题回顾:从k开始,移动m次。
1、准备两个变量,分别指向环形链表的首尾。
2、因为不一定是从编号1开始报数,所以需要先将第一步准备的两个变量移动到指定的位置(移动k次)。
3、循环m次,找到出列的编号,并将该节点删除。
4、重复第三步一直到环形链表中只剩下一个节点时结束。

代码:

/**
 * 计算出圈的顺序
 *
 * @param startNo 表示从第几个节点开始数
 * @param countNum 表示数几下
 * @param nums 表示圈内最初有几个节点
 */
public void calculateOutNode(int startNo, int countNum, int nums) {
    // 参数校验
    if (first == null || startNo < 1 || countNum > nums) {
      throw new RuntimeException("参数有误,请重新输入!");
    }
    // 创建辅助变量,并将helper指向链表尾部。
    Node helper = first;
    while (true) {
      if (helper.next == first) {
        break;
      }
      // 后移辅助变量
      helper = helper.next;
    }
    // 报数前让first和helper移动到指定位置
    for (int i = 0; i < startNo - 1; i++) {
      first = first.next;
      helper = helper.next;
    }
    // 开始出圈
    while (true) {
      if (helper == first) {
        // 环形链表中只剩下一个节点了,结束循环。
        break;
      }
      // 让first和helpe移动countNum次
      for (int i = 1; i < countNum; i++) {
        first = first.next;
        helper = helper.next;
      }
      // first指向的节点就是需要出列的节点
      System.out.println("编号为【" + first.no + "】的节点出列!");
      // 将first后移,并且将helper(指向环形链表尾部的节点)指向改变后的first节点,从而实现删除出列的节点。
      first = first.next;
      helper.next = first;
    }
    System.out.println("最后留在环形链表中的节点编号是【" + first.no + "】!");
}

通过以上代码可以轻松解决约瑟夫环问题。分分钟完成升职加薪,走向人生巅峰。

在这里插入图片描述

该吹的牛也吹了,今天的分享也该结束了。如果感觉文章写得还不错的,记得点赞、关注,多多鼓励鼓励哟!文章哪里写得有问题的话大家可以在评论区指出来,我会积极改正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值