面试题03. 数组中重复的数字
题目描述
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:
2 或 3
限制:
2 <= n <= 100000
解题思路
1. 数组排序(纯暴力),时间复杂度O(nlogn)
2. 哈希,时间复杂度O(n),空间复杂度O(n)(哈希表辅助)
3. 注意到数组中的数字是在0到n-1之间的,那么可以想一下,如果这个数组没有重复数字,那么排好序的数组中,i会出现在下标为i的位置。所以说,如果下标为i的位置的数字经过排序之后不是i,就说明出现了重复数字。从头到尾依次扫描数组中的每个数字,当扫描到下标为i的数字时,首先比较这个数字m是不是等于i。如果相等则扫描下一个数字,否则将m和下标为m的数字进行比较,如果两者相等则找到了重复数字,如果不等,则把m和下标为m的数字交换,重复比较直到找到重复的数字。对于每个数字,最多只需要交换两次,因此总时间复杂度是O(n)。而且所有步骤都在输入的数组上进行,所以不需要额外分配内存,空间复杂度为O(1)。这种方法也称作数组的“原地置换”。
C++版本
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
int length = nums.size();
if(length <= 0)
return false;
//这里书本写的很好,对异常进行了判断
for(int i = 0; i < length; i++)
{
if(nums[i] < 0 || nums[i] >= length)
return false;
}
//接下来开始循环比较
for(int i = 0; i < length; i++)
{
//对于数组中的一个数来讲,经过最多两次交换一定可以交换到它对应的位置,因此不会是死循环
while(nums[i] != i)
{
//将m,也就是nums[i],与nums[m],也就是nums[nums[i]]做比较,如果相同则找到了重复数字
if(nums[i] == nums[nums[i]])
{
return nums[i];
}
//否则,交换两者的位置
int t = nums[i];
nums[i] = nums[t];
nums[t] = t;
}
}
return false;
}
};
Java版本
class Solution {
public int findRepeatNumber(int[] nums) {
if(nums.length <= 0)
return -1;
//同样还是需要判断异常情况
for(int i = 0; i < nums.length; i++)
{
if(nums[i] >= nums.length || nums[i] < 0)
return -1;
}
//原地置换
for(int i = 0; i < nums.length; i++)
{
while(i != nums[i])
{
if(nums[i] == nums[nums[i]])
{
return nums[i];
}
int t = nums[i];
nums[i] = nums[t];
nums[t] = t;
}
}
return -1;
}
}