备战秋招-剑指offer打卡-不修改数组找出重复的数字(分治法)

给定一个长度为 n+1 的数组nums,数组中所有的数均在 1∼n 的范围内,其中 n≥1。

请找出数组中任意一个重复的数,但不能修改输入的数组。

样例

给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。

返回 2 或 3。

思考题:如果只能使用 O(1) 的额外空间,该怎么做呢?

题目链接(acWing)

思路:
不能修改数组,原来的方法,移动元素让下标和值相对应这个方法就用不了,又不能用额外的空间,也就是不能用打卡标记的方法了。正确的姿势是分治法,分的区间是数的范围,*如果在这个范围的数字个数大于区间长度,那么这个区间内一定有数字重复了。*继续对该区间进行划分,直到区间长度为1为止。时间复杂度:分治法为O(lgn),每次都要统计区间范围内的数字,复杂度为O(n)
所以总的复杂度为O(nlgn)。

1、如样例nums = [2, 3, 5, 4, 3, 2, 6, 7],分区间1-3,4-7;前面的区间范围内的数字个数为4,后面为3,很明显前面的区间内有数字重复了。
2、继续对前面的区间划分:1-2,3;前面的区间范围内的数字个数为2,后面为2,后面的区间有数字重复了(题目只让我们返回一个重复的数字,如果有多个随便挑一个就OK了)。
3、区间长度为1,个数却有两个,这个数字重复了,退出循环,返回该数字。

参考代码:

class Solution {
    public int duplicateInArray(int[] nums) 
	{
		int l=1,r=nums.length-1;
		int mid=(l+r)/2;
		while(l!=r)
		{
			int lNum=cnt(nums,l,mid);
			int rNum=cnt(nums,mid+1,r);
			if(lNum>mid-l+1)
				r=mid;
			else
				l=mid+1;
			mid=(l+r)/2;
		}
		return l;
    }

	private int cnt(int[] nums, int l, int mid) 
	{
		int count=0;
		for(int in:nums)
			if(in>=l&&in<=mid)
				count++;
		return count;
	}
}
发布了72 篇原创文章 · 获赞 19 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41022094/article/details/100849723