数组中重复的元素-剑指offer

题目描述:在一个长度为n的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入的长度为7的数组{2,3,1,0,2,5,3};那么对应的输出是重复的数字2或者3。

 看到这个题目首先想到的就是,先排序,排完序再找重复的数字,很easy。代码如下:
bool find_duplicate(int numbers[], int length, int &duplicate)
{
	if (numbers == NULL || length <= 0 || duplicate == NULL)
	{
		return false;
	}
	sort(numbers, numbers + length);//STL快速排序
	for (int i = 0; i < length - 1; i++)
	{
		if (numbers[i] == numbers[i + 1])
		{
			duplicate = numbers[i];
			return true;
		}
	}
	return false;
}
时间复杂度O(NlogN),空间复杂度O(1)。但时间复杂度过高。
看书上说可以用hash表来做这个,我忽然想到了set。set的底层可以用RB-tree,也可以用hash。原理类似。代码如下:
bool find_duplicate(int numbers[], int length, int &duplicate)
{
	if (numbers == NULL || length <= 0 || duplicate == NULL)
	{
		return false;
	}
	set<int> s;
	set<int>::iterator it;
	s.insert(numbers[0]);
	for (int i = 1; i < length; i++)
	{
		if ((it = s.find(numbers[i])) != s.end())
		{
			duplicate = numbers[i];
			return true;
		}
		else
		{
			s.insert(numbers[i]);
		}
	}
	return false;
}
由于set的插入,查找,删除时间复杂度均为O(logN),所以平均时间复杂度为O(N)。空间复杂度为O(N)。这是增加了额外的内存空间,也不好。
下面来看看书上说的时间复杂度为O(N),空间复杂度为O(1)的算法。
首先从头到尾扫描这个数组的每个元素。当扫描到下标为i的元素时,首先比较这个元素(记为m)是不是等于索引i。如果是,接着扫描下一个元素。如果不是,再拿它和索引为m的元素比较。如果它和索引为m的元素相等。就找到了一个重复的数字了。此时停止扫描即可。如果它和索引为m的元素不相等,那么就把它和索引为m的元素交换,把m放到索引为m的地方去。接下来重复比较,交换。直到发现一个重复的数字。
 为什么这样就行呢?首先根据题目要求,长度为n数组,所以元素都在0-n-1范围。比如有数组array_test[5]={1,0,3,2,4},这种情况没有元素重复,经过上述操作,array_test[5]={0,1,2,3,4}.所以索引对应的元素就是索引本身的值。但是一旦有重复的元素,比如array_test[5]={0,1,2,2,3}(该种情况已经排好)。不同的索引会对应同一个值,比如索引2对应的值为2,索引3对应的数值也为2。那么扫描到索引3,里面的元素为2。再到索引为2的地方查找,发现对应的元素也为2.就找到了重复的一个数字。这种方法的目的就是把一个打乱的数组调整为索引==内容。理论上是一个萝卜一个坑,但可惜不是,哈哈~。所以~~~~~~代码如下:
bool find_duplicate(int numbers[], int length, int &duplicate)
{
	if (numbers == NULL || length <= 0 || duplicate == NULL)
	{
		return false;
	}
	for (int i = 0; i < length; i++)
	{
		if (numbers[i] != i)
		{
			if (numbers[numbers[i]]!=numbers[i])
			{
				int temp = numbers[i];
				numbers[i] = numbers[numbers[i]];
				numbers[temp] = temp;
			}
			else
			{
				duplicate = numbers[i];
				return true;
			}
		}
	}
	return false;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值