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