写在前面
题目:
有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号。
例如;如果10个人 留下的是4号
这里我们使用数组来实现它。
数组初始化值从1开始,如果有10个人,那么数组中存储的值就是[1-10]
每次删除一个元素,我们就把该元素的值标记为-1
(这里我们统一约定,值不为-1的元素就是可用元素)
10个人的话,删除到最后,就只剩下一个可用元素,它的值为4
实现的思路:
写代码前我们都要先做一番思考,搞清楚实现它的流程,如果在没有任何思路甚至没有理清题意的情况下去写代码,写出来的都是没有任何价值的字符串罢了。
题目要求是n个人围成一圈,我们假定n = 10
初始化的数组长这样:
数3个数字删一次,我们先删3次再说:
可以看到,3,6,9被删除,它们的值也变成-1
每次删除元素的时候,我们都是通过遍历数组来做到的。
遍历数组的每一个元素
如果该元素的值为-1,表示它已经删除,我们不做任何操作
如果该元素的值不是-1,表示它是可用元素,我们数一次
数到第三个,就删除那个元素(给它赋值为-1)
那么问题来了,数组遍历到了最后,我该如何让它继续删?
现在应该从10开始数,数3个再删,所以,很自然得想到,不妨把10挪动位置,让它放在数组的前面。
像这样:
然后继续做与刚才的操作。
依次类推,直到数组中可用元素只有一个,就结束循环。
问题是,我怎么知道什么时候需要挪动数组的元素,什么时候不需要?
这就需要一个判断,每次删除一个元素时,我得知道这个元素后面还有几个可用元素(我在代码中写了一个方法来做这一步),如果它后面的可用元素数量小于3且大于0,也就是说,只有当可用元素的数目为1或2的时候,我们才去挪动后面的元素(挪动元素,也设计了一个方法来做)。
整体代码
好了,整体思路就是这样,它可以认为是最笨的办法,并且它底层是利用小数组来挪动数组的元素,所以它的空间复杂度、执行效率等并不是很优化。
整体代码如下:
package com.briup.day09;
import java.util.Arrays;
public class Que1 {
/**
* 有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号。
* 例如;如果10个人 留下的是4号
*/
public static void main(String[] args) {
Que1 que1 = new Que1();
que1.test(10);//以10个人来测试
}
public void test(int totalNum) {
int[] array = new int[totalNum];
//赋初始值,例如10个人,初始值:【1,2,3,4,5,6,7,8,9,10】
for(int i = 0;i<array.length;i++) {
array[i] = i+1;
}
int size = array.length;//值不为-1的元素个数,即可用元素个数
int flag = 0;//用来计数,数1,2,3的
while(size > 1) {
for(int i = 0;i<array.length;i++) {
if(array[i] != -1) {
flag++;
if (flag == 3) {
flag = 0;//重新变成0
array[i] = -1;//标记这个元素已经删除,(模拟删除)
size--;//可用元素个数减1
//删除完以后,判断后面有可用元素,我们让一个小弟来做
int behindSize = getBeginIndexBehindSize(array,i);
if(behindSize<3 && behindSize >0) {
//把后面的元素,全部挪动到前面(不管是可用的,还是非可用的,都挪动)
//让一个小弟来做
//System.out.println("\t挪动前:"+Arrays.toString(array));
moveArrayElements(array, i);//移动了数组的元素
//System.out.println("挪动后:"+Arrays.toString(array));
break;
}
}
}
}
}
System.out.println(Arrays.toString(array));
}
//设计一个方法,给一个数组,给一个起始点,判断起始点后面(不包括起始点)有几个不为-1的元素
private static int getBeginIndexBehindSize(int[] arr,int begin) {
if (begin > (arr.length-1)) {
throw new RuntimeException("begin参数的值超过了数组的索引范围");
}
int result = 0;
for(int i = begin+1;i<arr.length;i++) {
if (arr[i] != -1) {
result++;
}
}
return result;
}
//设计一个方法,给定一个数组,给定一个起始点,把数组从起始点开始,不包括起始点,后面的所有元素,挪动到数组的前面,前面的值相应地往后挪
private static void moveArrayElements(int[] arr,int begin) {
if (begin > (arr.length-1)) {
throw new RuntimeException("begin参数的值超过了数组的索引范围");
}
//创建两个小数组,用来装部分元素
int[] a = new int[begin+1];//前面那部分
int[] b = new int[arr.length-begin-1];//后面那部分
//创建一个大数组,用来装全部元素
//int[] newArr = new int[arr.length];
for(int i = 0;i<a.length;i++) {
a[i] = arr[i];
}
for(int i = 0;i<b.length;i++) {
b[i] = arr[i+begin+1];
}
//覆盖原来的大数组
for(int i = 0;i<arr.length;i++) {
if (i<b.length) {
arr[i] = b[i];
}else {
arr[i] = a[i-b.length];
}
}
}
}
执行结果如下: