剑指 Offer 03. 数组中重复的数字
题目描述
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入:[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
限制:
2 <= n <= 100000
解题思路
本题不仅考查coding能力,还考查应聘者的沟通能力,应与面试官确定是否可以修改原属组,以及时间和空间的要求。如果可以修改原数组,则直接利用数组下标进行元素替换;如果不能修改原数组,则考虑时间和空间的tradeoff,下面具体讲。
1. 暴力搜索
先排序,然后遍历数组,直到找到重复元素。
暴力算法的时间复杂度O(nlogn)(数组排序的时间复杂度)。
2. 可修改原数组的算法
遍历数组,遇到下标为i的数字m,首先将m与下标i进行比较,若相等,则继续遍历下一个数字;若不相等,则将数字m与num[m](即下标为m的数字)进行比较,若m==num[m],则说明发现重复数字;若不相等,则将数字m与num[m]进行交换,讲数字m放到属于它的位置上。
class Solution {
public int findRepeatNumber(int[] nums) {
if(nums==null || nums.length<=0)
return -1;
int len = nums.length;
for(int i=0; i<len; i++){
if(nums[i]==i){
continue;
}
else if(nums[i]==nums[nums[i]]){
return nums[i];
}
else{
int temp = nums[i];
nums[i] = nums[temp];
nums[temp] = temp;
}
}
return -1;
}
}
3. 不可修改原数组的算法
(1)辅助空间
一种空间换时间的方法是,使用O(n)的辅助空间,创建一个长度为n+1的辅助数组,然后逐一把原数组的每个元素复制到辅助数组。同时,如果原数组的数字为m,则将其复制到新数组下标为m的位置。这样很容易就可以找到重复数字。
(2)二分查找
将1~n的数字从中间的数字m分为2部分,第一部分是1~m,第二部分是m+1~n。如果1~m数字的个数超过m个,则这个区间内一定含有重复数字。不过这个方法不能保证找出所有重复数字,每次countRange()方法时,只能统计在start~end区间内的数字个数。
注意:此方法无法找到所有重复的数字,无法通过leetcode所有测试用例。
例如:[0, 1, 2, 0, 4, 5, 6, 7, 8, 9],在计数0~4的count时count=5,与数字数目相等,因此下一轮变为start = mid+1,导致输出结果错误。
class Solution {
public int findRepeatNumber(int[] nums) {
if(nums==null || nums.length<=0)
return -1;
int len = nums.length;
int start = 0; //数字0,不是下标
int end = len-1;
while(start<=end){
int mid = ((end-start)>>1)+start;
int count = countRange(nums, start, mid);
if(start==end){ //只剩一个元素了
if(count>1)
return start;
else
break;
}
if(count>(mid-start+1))
end = mid;
else
start = mid+1;
}
return -1;
}
public int countRange(int[] nums, int start, int end){
if(nums==null)
return 0;
int len = nums.length;
int count = 0;
for(int i=0; i<len; i++){
if(nums[i]>=start && nums[i]<=end)
count++;
}
return count;
}
}