1. 问题描述
设编号为 1,2,3,…,n 的 n 个人围坐在一起,让编号为 k (1 <= k <= n) 的人从 1 开始报数,数到第 m 个的这个人出列,然后从此人的下一人再从 1 开始报数,数到 m 的出列,以此类推,直到所有人都出列。打印出整个过程的出队编号序列。
2. 解决思路
- 利用 单向环形链表
- 步骤:
- 初始化环形链表 和 出队数组
- 校验:是否都是正数;startNodeData是否在链表长度范围内
- 设置preNode,作为firstNode的上一个节点
- 将first移动到节点值为 k 的位置,pre跟随其后 (移动 k - 1)
- 开始报数,循环遍历,直到只剩一个节点停止
5.1 first和pre同时移动 countNumber - 1 位
5.2 firstNode出圈,加入出队数组,并打印
5.3 将 firstNode 指向下一位 - 添加并打印所剩的一个节点值
3. 代码实现
/**
* 约瑟夫问题:
* 设编号为 1,2,3,...,n 的 n 个人围坐在一起,让编号为 k (1 <= k <= n) 的人从 1
* 开始报数,数到第 m 个的这个人出列,然后从此人的下一人再从 1 开始报数,数到 m 的出
* 列,以此类推,直到所有人都出列。打印出整个过程的出队编号序列。
*
* @author hoy
*/
public class JosephProblem {
private static CircleSingleLinkedList circleSingleLinkedList;
private static SingleNode[] dequeueArray;
/**
* 约瑟夫问题的解决方法
* @param startNodeData - 开始报数的人的编号 k
* @param countNumber - 每次应报的数
* @param initialLength - 链表的初始长度
*/
public static SingleNode[] solution(int startNodeData, int countNumber, int initialLength) {
// 1. 初始化环形链表 和 出队数组
circleSingleLinkedList = new CircleSingleLinkedList();
circleSingleLinkedList.initializeForJoseph(initialLength);
SingleNode firstNode = circleSingleLinkedList.getFirstNode();
dequeueArray = new SingleNode[initialLength];
// 2. 校验:是否都是正数;startNodeData是否在链表长度范围内
if (countNumber <= 0 || initialLength <= 0 || startNodeData < 1 || startNodeData > initialLength) {
throw new RuntimeException("input data error!");
}
// 3. 设置preNode,作为firstNode的上一个节点
SingleNode preNode = firstNode;
while (preNode.getNextNode() != firstNode) {
preNode = preNode.getNextNode();
}
// 4. 将first移动到节点值为 k 的位置,pre跟随其后 (移动 k - 1)
for (int i = 0; i < startNodeData - 1; i++) {
firstNode = firstNode.getNextNode();
preNode = preNode.getNextNode();
}
// 5. 开始报数,循环遍历,直到只剩一个节点停止
int count = 0;
while (firstNode.getNextNode() != firstNode) {
// 5.1 first和pre同时移动 countNumber - 1 位
for (int i = 0; i < countNumber - 1; i++) {
firstNode = firstNode.getNextNode();
preNode = preNode.getNextNode();
}
// 5.2 firstNode出圈,加入出队数组,并打印
preNode.setNextNode(firstNode.getNextNode());
dequeueArray[count] = firstNode;
count++;
System.out.println(firstNode.toString());
// 5.3 将 firstNode 指向下一位
firstNode = firstNode.getNextNode();
}
// 6. 添加并打印所剩的一个节点值
dequeueArray[count] = firstNode;
System.out.println(firstNode.toString());
return dequeueArray;
}
}