问题描述:
N个人围成一圈顺序编号,从1号开始按1、2、3......顺序报数,报p者退出圈外,其余的人再从1、2、3开始报数,报p的人再退出圈外,以此类推。
请按退出顺序输出每个退出人的原序号。
输入格式:
输入只有一行,包括一个整数N(1<=N<=3000)及一个整数p(1<=p<=5000)。
输出格式:
按退出顺序输出每个退出人的原序号,数据间以一个空格分隔,但行尾无空格。
输入样例:
在这里给出一组输入。例如:
7 3
输出样例:
3 6 2 7 5 1 4
解题思路:
用不带头结点,只带尾指针的循环链表处理约瑟环问题。
1、定义空链表
2、依次放入n个元素,构成循环链表
3、从链表当前位置开始,找到第m个结点,删除它,保存它的元素值到结果序列中。
4、重复第3步,直到处理结束 。
处理结束,怎么表达呢?是把链表删空吗?
因为不带头结点的循环链表中,删除唯一的结点,会使链表变成空表,它的操作语句与大于1个元素时的删除操作不一样,因此可以在链表中只剩下1个结点时结束处理。
因此,这个结束标志可以是:链表只剩下1个结点,也可以用已产生了n-1个结果。
5、输出结果序列中的值。
代码如下:
import java.util.Scanner;
class LinkNode<E> {
E data;
LinkNode<E> next;
public LinkNode(E data, LinkNode<E> next) {
super();
this.data = data;
this.next = next;
}
public LinkNode(E data) {
super();
this.data = data;
this.next = null;
}
public LinkNode() {
super();
this.next = null;
}
}
public class JosephLoop {
public static void josephLoop(int n, int m) {
// 构造约瑟夫环,形式为不带头结点的循环链表,rear为尾指针
LinkNode<Integer> rear = new LinkNode<Integer>(1);
rear.next = rear;
for (int i = 2; i <= n; i++) {
rear.next = new LinkNode<Integer>(i, rear.next);
rear = rear.next;
}
// result用于保存最终出列的元素,也可以定义为队列类型
Integer[] result = new Integer[n];
int j = 0;
while (rear.next != rear) { //也可以用j<n-1做为循环条件
//寻找第m个元素,rear的后继
for (int k = 1; k < m; k++)
rear = rear.next;
//将第m个元素添加到结果序列中
result[j++] = rear.next.data;
//把第m个元素从循环链表中删除
rear.next = rear.next.next;
}
result[j] = rear.next.data; //循环队列中的最后一个元素也要保存
// 输出出列的结果,按要求格式,最后一个元素之后没有空格
for (j = 0; j < n - 1; j++)
System.out.print(result[j] + " ");
System.out.println(result[j]);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
josephLoop(n, m);
}
}