【剑指offer】——数组中的重复数字

题目要求:

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

解析:

方法一:
先将数组排序,从排序的数组中找出重复的数字是一件很容易的事情,但是这样时间复杂度O(nlogn)很高

方法二:
因为长度为n的数组里所有数字都在0~n-1的范围内,如果没有重复的数字的话,那么数组排序后数字i将出现在下标为i 的位置。但是由于有重复的数字,所以有些位置可能存在多个数字,有些位置可能没有数字。
我们先拿一个具体的例子,来描述一下我们的这个方法
例如数组{2,3,1,0,2,5,3}。第一个数为2 != 下标0,所以把2交换到下标为2的位置去。
在这里插入图片描述
i++,继续比较,第二个数3 != 下标1,所以把3交换到下标为3的位置去
在这里插入图片描述
i++,继续比较,第三个数2 == 下标2,不交换。
i++,继续比较,第四个数3 == 下标3,不交换。、
i++,继续比较,第五个数2 != 下标4,比较number[4] == number[numver[4]].。因此找到了一个重复的值
代码实现:

bool duplicate(int number[], int length, int* duplicate)
{
	if (number == nullptr || length <= 0)
	{
		return false;
	}
	for (int i = 0; i < length; i++)
	{
		if (number[i]<0 || number[i]>length - 1)
		{
			return false;
		}
	}
	for (int i = 0; i < length; i++)
	{
		while (i != number[i])
		{
			if (number[i] == number[number[i]])
			{
				*duplicate = number[i];
				return true;
			}
			int tmp = number[i];
			number[i] = number[tmp];
			number[tmp] = tmp;
		}
	}
	return false;
}	

题目改进:

在上述题目的基础上要求不能输入的数组

解析:

方法一:
我们创建一个长度为n+1的辅助数组,然后逐一把原数组的每一个数字复制到辅助数组,把值为m的数字复制到小标为m的位置。但是这样做的空间复杂度为O(n)。所以我们想出了另外一个方法

方法二:
还是以具体的数组来加以说明,例如数组{2,3,5,4,3,2,6,7}。有8个数字。中间的4把数组分成两部分,一段是1-4,另一段是5-7.
在这里插入图片描述
1-4里面的个数大于后者,所以重复数字一定在1-4之间。
再把中间的2,分成两组,1-2,3-4
在这里插入图片描述
3-4里面的个数大于前者,所以重复数字一定在3-4之间。
再分别统计这两个数字在数组中出现的次数。

代码实现如下:

int getcount(int* number, int length, int start, int end)
{
	if (number == nullptr)
		return 0;
	int count = 0;
	for (int i = 0; i < length; i++)
	{
		if (number[i] >= start && number[i] <= end)
		{
			count++;
		}
	}
	return count;
}
int getduplicate(int* number, int length)
{
	if (number == nullptr || length <= 0)
		return -1;
	int end = length - 1;//7
	int start = 1;//1
	while (end >= start)
	{
		int middle = ((end - start) >> 1) + start;//4
		int count = getcount(number, length, start, middle);
		if (end == start)
		{
			if (count > 1)
				return start;
			break;
		}
		if (count > (middle - start + 1))//5>4 所以在1-4里面再划分
			end = middle;
		else
			start = middle;
	}
	return -1;
}

这种方法类似于二分查找,函数getcount被调用O(logn)次,每次需要O(n)的时间,所以时间复杂度为O(nlogn).但是空间复杂度是O(1)。这是一个时间换空间的算法。

发布了98 篇原创文章 · 获赞 9 · 访问量 3660

猜你喜欢

转载自blog.csdn.net/qq_43412060/article/details/105254115