经典算法题:有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号。

写在前面

题目:

有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];
			}
		}
	}
}

执行结果如下:
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值