一、问题描述
设编号为 1 ~ n( n > 0 )的 n 个人按顺时针方向围成一圈,约定编号为 k 的人从 1 开始顺时针报数,报 m 的人( m 为正整数),令其出列。然后从他的下一个人开始,重新从 1 顺时针报数,报 m 的人,再令其出列,如此下去,直到圈中所有人都出列为止。求出列编号序列。
二、示例
三、思路及代码实现
1. 构造单向环形列表
思路:
- 先创建第一个节点,让 first 指向该节点,并构建成环形
- 后面每创建一个新的节点,将该节点加入到已有的环形链表中即可
图示:
代码如下:
/**
* 构建单向环形链表
* @param nums 队列人数
*/
public void circle(int nums) {
//数据校验,如果传入的数据不合法,提示并结束方法
if (nums < 1) {
System.out.println("nums的值不正确!无法创建环!");
return;
}
//初始化中间节点
Node temp = null;
for (int i = 1; i <= nums; i++) {
//用来存放每次新创建的编号节点
Node node = new Node(i);
//如果是第一个节点,则直接赋值给first,且构建成环状
if (i == 1) {
first = node;
first.next = first;
temp = first;
continue;
}
//如果不是第一个节点,则将其添加进环中
temp.next = node;
node.next = first;
temp = node;
}
}
2. 遍历环形链表
思路:
- 先让一个辅助节点 temp 指向 first节点
- 然后通过一个 while 循环遍历该环形链表即可(如果 temp.next==first 则结束循环)
代码如下:
/**
* 遍历打印环形链表
*/
public void list() {
//如果第一个节点没有数据,则链表为空,提示并结束方法
if (first == null) {
System.out.println("链表为空!");
return;
}
//借助辅助节点遍历
Node temp = first;
while (temp.next != first) {
System.out.println(temp);
temp = temp.next;
}
System.out.println(temp);
}
3. 约瑟夫环问题解决
思路:
- 先创建一个辅助节点 temp 指向当前链表的最后一个节点
- 在开始报数前,先让 temp 与 first 同时移动 k-1 次
- 当开始报数时,让 temp 与 first 同时移动 m-1 次
- 将 first 指向的编号出列,first 指向出列节点的下一个节点
图示:
代码如下:
/**
* 计算约瑟夫环的出列序列
* @param k 第k个节点开始
* @param m 开始后第m个节点出列
* @param n 一共有n个节点
*/
public void joseph(int k, int m, int n) {
//数据校验,如果数据不合法则提示且结束方法
if (first == null || k < 1 || k > n) {
System.out.println("数据不合法!");
return;
}
//辅助节点
Node temp = first;
//找到环形链表的最后一个节点(下一个节点为 first 节点的节点),并赋值给辅助节点
while (temp.next != first) {
temp = temp.next;
}
//在开始报数前,先让 temp 与 first 同时移动 k-1 次
for (int i = 0; i < k - 1; i++) {
first = first.next;
temp = temp.next;
}
//当 temp == first 时,环形链表中只剩下一个节点,结束循环
while (temp != first) {
//找到报数为 m 的节点
for (int i = 0; i < m - 1; i++) {
first = first.next;
temp = temp.next;
}
//输出当前节点编号,且将当前节点出列
System.out.printf("%d-->", first.id);
first = first.next;
temp.next = first;
}
//输出最后节点编号
System.out.println(first.id);
}
四、测试
代码如下:
/**
* 测试结果:
* Node{id=1}
* Node{id=2}
* Node{id=3}
* Node{id=4}
* Node{id=5}
* -----------------------
* 4-->1-->3-->2-->5
*/
public static void main(String[] args) {
JosephRing ring = new JosephRing();
//构建环形列表
ring.circle(5);
//遍历环形链表
ring.list();
System.out.println("-----------------------");
//打印约瑟夫环编号序列
ring.joseph(3, 2, 5);
}