(你能想到所有解法吗?)
找出数组中重复的数字
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例:
输入:[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
限制:
2 <= n <= 100000
解法详谈:
题目看似简单,但实际上是考察对空间复杂度以及时间复杂度的掌握是否到位,下面我们来进行详细的分析(代码均为C语言):
方法一: 使用双重循环,对数组nums中的值依次遍历,查找之后是否存在相同元素,有的话就直接return。这是最笨的方法,当数组较大时,执行起来非常慢,时间复杂度O(n^2),空间复杂度O(1)
int findRepeatNumber(int* nums, int numsSize){
for(int i = 0;i<numsSize;i++)
for(int j = i+1;j <numsSize;j++)
if(nums[i]==nums[j])
return nums[i];
return 0;
}
方法二: 先对数组nums进行排序(这里选择的快排),然后看相邻元素是否有相同的,有相同元素的话就直接return。 执行较慢,时间复杂度O(nlogn),空间复杂度O(1)
int findRepeatNumber(int* nums, int numsSize){
qsort(nums, numsSize, sizeof(int), comp);
for (int i = 1; i < numsSize; i++) {
if (nums[i - 1] == nums[i])
return nums[i];
}
return 0;
}
方法三: 前两种方法在时间复杂度方面都较高,如果想降低时间复杂度。题目中表示数组nums中的数值在 0~n-1 的范围内,那我可以采用典型的以空间换时间的方法,声明一个长度为n的辅助数组a,以nums数组中数值来作为数组a的下标,从而对数组a中值进行判断是否存在相同元素。这里提醒大家的是一般碰到数组nums中的数值在 0~n-1 的范围内 这种条件时,都是可以考虑此种方法。
int findRepeatNumber(int* nums, int numsSize){
int *a=(int *)malloc(sizeof(int)*numsSize);
int i,j;
for(i=0;i<numsSize;i++){
a[i]=0;
}
for(i=0;i<numsSize;i++){
if(a[nums[i]]==0){
a[nums[i]]++;
}else{
return nums[i];
}
}
return 0;
}
方法四:题目中表示数组nums中的数值在 0~n-1 的范围内,所以数组的值和下标大小范围是一样的。那我们可以从头到尾扫描这个数组中的每个数字,当出现改位置数字不相等的时候,假设当前索引为i,值为j,若 i ≠ j i \neq j i=j,则交换i和j。若二者相等,说明找到一个重复数字。这么交换下去,直到碰到重复数字,或者整个遍历结束。
int findRepeatNumber(int* nums, int numsSize){
for(int i = 0;i<numsSize;i++)
while(i!=nums[i]){
if(nums[i]==nums[nums[i]])
return nums[i]
else{
temp = nums[i]
nums[i] = nums[nums[i]]
nums[temp] = temp
}
}
return 0;
}
值得注意的是,当题目中出现了数值在 0~n-1 的范围内 大家就要能想起方法三、四,并加以运用。
上述方法中在查找重复元素时,碰到首个重复元素就停止查找了,如果想找出所有的重复元素该怎么办呢?