问题
有n个人围成一圈,顺序排号。从现在第i个人开始,由1至k不断报数,凡报到k的人出列。重复报数的过程,直到所有人都出列为止。请编写程序模仿这n个人的出列顺序。
分析
先理解题目 :n个人围成一个圈,每个人排序,从位置i(手动输入)开始从1开始报数,报到k(手动输入)的时候这个人就退出去,然后这个时候接着重新从退出去的位置从1开始报数,接着找下一个报数到k的人出去,依次往复。
找序列规律
这里有的关键问题:就是将K控制在1圈内执行,比如n为5 k为18 ,正常需要转3圈之后才退出去,而通过求余数 18 % 5 = 3 就能找到当前开始退出的位置。
例如
n = 5 k = 18 i = 3;(以下不理解可以按圆圈画图)
初始序列 1 2 3 4 5
序列: 3 4 5 1 2 (从 i = 3 开始)
第一圈 退出的是 18 % 5 = 3(第三个位置)也就是 5号
新序列 1 2 3 4
第二圈 退出的是 18 % 4(n) = 2 (当前圈的位置二)也就是2号
新序列 3 4 1
第三圈 退出的是 18 % 3(n) = 0 (整除取最后) 也就是 1号
新序列 3 4
第四圈 退出的是 18 % 2(n) = 0 (整除取最后) 也就是 4 号
最后 退出 3号
约瑟夫环规律
Java实现
找序列规律
public static void circle(){
int n = 5,k = 18,i = 3;
//初始编号
int[] people = new int[]{1,2,3,4,5};
int start = i-1;
int[] a = new int[n];
while (n > 0){
//重新排序
int m = 0;
for (int j = start; j < people.length; j++) {
if (people[j] != 0) a[m++] = people[j];
}
for (int j = 0; j < start; j++) {
if (people[j] != 0) a[m++] = people[j];
}
for (int j = 0; j < people.length ; j++) {
if (j < n) people[j] = a[j];
else people[j] = 0;
}
//退出的号码,退出的成员设置为0
if (k % n == 0) {
System.out.println(people[n-1]);
people[n-1] = 0;
}else {
System.out.println(people[k%n-1]);
people[k%n-1] = 0;
}
start = k % n;
n--;
}
}
- 时间复杂度:O(n^2)
- 空间复杂度:O(n)
约瑟夫环规律
public static void test08(){
int n = 5 , k = 18 ,i = 3,m = n;
boolean[] p = new boolean[n];
int pNext = i-1;
while (m > 0){
//转k圈
int a = 1;
while(a < k){
if (!p[pNext]) {
a++;
pNext++;
}else {
pNext++;
}
if (pNext == n) pNext = 0;
}
while (p[pNext]){
pNext++;
if (pNext == n) pNext = 0;
}
System.out.print(pNext+1+" ");
p[pNext] = true;
m--;
}
}