先赞后看,养成习惯!
最近闲来无事,翻看了一下之前面试后做的记录,发现当时的自己真是“菜的一批”啊(虽说现在也挺菜,但是多少还是有那么一点点进步的)。追忆一下当年有点跑题了哈哈哈,今天是想跟大家分享一下当年遇到的一道面试题:“约瑟夫环问题”。
多得不说,少得不唠,下面开始今天的分享。
什么是约瑟夫环问题
大家知道什么是约瑟夫环吗?
各位读者是不是心里想着:“知道谁还看你在这废话。”然后悄悄地攥紧了拳头?别,各位大哥大姐千万别激动,给小弟点时间,让小弟为你排忧解难。
约瑟夫问题为:设编号为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 + "】!");
}
通过以上代码可以轻松解决约瑟夫环问题。分分钟完成升职加薪,走向人生巅峰。
该吹的牛也吹了,今天的分享也该结束了。如果感觉文章写得还不错的,记得点赞、关注,多多鼓励鼓励哟!文章哪里写得有问题的话大家可以在评论区指出来,我会积极改正!