自学算法之不修改数组找出重复数字

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37014990/article/details/79947892

题目:

在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但是不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。

看到题目,总觉得有种似曾相识的感觉,但却不会做,痛定思痛,于今日写下总结:

当然,很多读者包括笔者自己,看算法博客一般都喜欢看最优方案,遇到什么方案X之类的,直接跳到最后.....

ok,请读者不用滑动飞轮,以下就是据笔者所知,最优的方案:

方案一,二分查找 + 计数。

咳咳,笔者假装解释一下该算法的思路:

做人要讲道理,所以算法也要。

且看数组 arr = [1, 2, 3, 4, 5],再看题设,长度为 n + 1的数组,得出 n = 5。

又有,数字在 1~n 之间,所以数组中的数字,只能是在区间 [1, 5]之间。

统计每个数字在整个数字出现的次数:

计数数值: 1,2,3,4,5    ---------长度范围为5

出现次数: 1,1,1,1,1    ---------出现次数总和:1 + 1+ 1 + 1 + 1 = 5

明显,5 = 5。

没错,arr中没有重复数字!..........emmmm

哎,说了半天,笔者也不知道再说些什么了....但还是要硬着头皮往下写.....

在此,笔者厚颜向读者提出一个问题:为什么数组中会出现重复数字?

讲道理,假设数组中没有重复数组,范围1~n中数字的个数就是n,由于数组中包含超过n个数字,所以一定包含重复数字。

额.....有点抽象。

不慌,请读者再看数组 arr2 = {2,3,5,4,3,2,6,7},所有数字都在1~7之间,中间的4把数组分成两段(折半)。

第一段:{2,3,5,4}

第二段:{3,2,6,7}

分段后,开始计数,即统计每一段中的数字,在整个数组(没有折半前,即arr2)中出现的次数。

第一段计数开始:

计数数值: 2,3,5,4    ---------长度范围为4 

出现次数: 2,2,1,1    ---------出现次数总和:2 + 2 + 1 + 1 = 6

很明显,6 > 4 , 说明第一段中绝逼有重复数字!

然后,继续折半,计数。

	/**
	 * 折半,计数部分
	 */
	public static int isCount(int[] arr, int start, int end){
		//参数检查
		if(arr == null) return -1;
		if(start > end) return -1;
		int count = 0; //计数,数字出现的次数
		//计数开始
		for(int i = 0, len = arr.length; i < len; i++){
			if(arr[i] >= start && arr[i] <= end){
				count++;
			}
		}
		return count;
	}
	
	public static int getDupFunction(int[] arr){
		int start = 1; //起始
		int end = arr.length - 1; //结束
		while(end >= start){
			 //右移
			int middle = ((end - start) >> 1) + start;
			//计数
			int count = isCount(arr, start, middle);
			//首尾相等
			if(end == start){
				if(count > 1){
					return start; //当前数
				}else{
					break;
				}
			}
			//前一段
			int part = middle - start + 1;
			if(count > part){  //满足,说明(start, middle)区间中存在重复数字
				end = middle; //计算下标, (start , middle)
			}else{ //后一段存在重复数字
				start = middle + 1; 
			}
		}
		return -1;
	}
	
	public static void main(String[] args) {
		
		int n;
		@SuppressWarnings("resource")
		Scanner in = new Scanner(System.in);
		System.out.println("输入数组长度:");
		n = in.nextInt();
		
		if(n < 0) return ;
		int[] arr = new int[n];
		for(int i = 0; i < n; i++){
			System.out.println("请输入数组第" + (i + 1) + "个数:");
			arr[i] = in.nextInt();
		}
		int sub = getDupFunction(arr);
		System.out.println("重复数数字:" + sub);
	}

写完代码后,你会发现,该代码只能找出重复数字3,但是2找不出来。

所以,该算法,不能保证找出所有重复数字。

好了,看完方案一,我们再看方案二:

方案一,空间换时间,即创建一个n + 1长度的数组,将原数组每一项的值放在,与之下标对应的位置。

Such As,数值为2的应该放在arr[2],数值为3的应该应该放在arr[3]中,以此类推......

写到这里,笔者也不想写了,该方案很多博客上都有。

所以........

	/**
	 * 空间换时间
	 */
	public static void main(String[] args) {
		
		int[] arr = {2, 3, 5, 4, 3, 2, 6, 7};
		int[] tempArr = new int[arr.length + 1];
		
		for(int i = 1; i <= arr.length; i++){
			if(tempArr[arr[i]] != arr[i]){
				tempArr[arr[i]] = arr[i];
			}else{
				System.out.println("重复数字:" + arr[i]);
			}
		}
	}






猜你喜欢

转载自blog.csdn.net/qq_37014990/article/details/79947892