注意:要问面试官时间/空间以及是否能修改原数组的需求。
解法1:排序然后找出重复的数字,时间复杂度O(nlogn) 不需要额外空间
解法2:哈希表,unordered_set。
解法3:数组实现哈希表,因为规定了所有数字都在0~n-1范围内。时间复杂度O(n),空间复杂度O(n)
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
vector<int>map(nums.size(), 0);
for(int i : nums){
map[i]++;
if(map[i] > 1)
return i;
}
return -1;
}
};
解法4:原地哈希,不需要额外空间,时间复杂度为O(n)
●数字nums[i] 应该放在下标为 i 的位置上,这就像是我们人为编写了哈希函数,这个哈希函数的规则还特别简单;
而找到重复的数就是发生了哈希冲突;
●类似问题还有「力扣」第 41 题: 缺失的第一个正数、「力扣」第 442 题: 数组中重复的数据、「力扣」第 448 题: 找到所有数组中消失的数字 。
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
for(int i = 0; i < nums.size(); i++){
while(nums[i] != i){
//nums[i]应该放在下标为nums[i]的位置上,如果重复则找到元素
if(nums[nums[i]] == nums[i])
return nums[i];
//不重复则把nums[i]放到该放的地方
swap(nums[nums[i]], nums[i]);
}
}
return -1;
}
};
题目如果变为长度为n+1个整数数组数字在1到n之间(包括1和n),可以用二分查找做,因为上面0到n-1有n个数,可能会出现缺少的数和重复的数出现在一边,没法缩小范围。
此题中原地哈希仍然适用,但是不符合不能修改原数组的需求。
解法1:二分查找:时间换空间的做法,不常用,不过可以满足题目不能更改数组,且不能使用额外空间的需求
class Solution {
public:
int cnt(vector<int>& nums, int mid){
int res = 0;
for(int i : nums)
if(i <= mid)
res++;
return res;
}
int findDuplicate(vector<int>& nums) {
if(nums.size() < 2)
return -1;
int left = 1, right = nums.size() - 1;
while(left < right){
int mid = left + (right - left) / 2;
//如果<=mid的数<=mid,如<=4的数只有4个,那么重复的数比4大
if(cnt(nums, mid) <= mid)
left = mid + 1;
else
right = mid;
}
return left;
}
};
解法2:快慢指针
双指针解法,可以将数组看成一个链表,类似于环形链表找环入口,快指针肯定追上慢指针,L = m +n
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int slow = 0, fast = 0;
while(true){
slow = nums[slow];
fast = nums[nums[fast]];
if(slow == fast)
break;
}
fast = 0;
while(true){
slow = nums[slow];
fast = nums[fast];
if(slow == fast)
break;
}
return slow;
}
};