【剑指offer】 03 数组中重复的数字
3-1 题目
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
思考
- 解法1:我直接一看就打算暴力了,开一个数组or字典记录每个出现过的数字,直接扫一边即可获得答案,时间复杂度为O(n),空间复杂度也是O(n)
- 我没有考虑其他的解法,还有其他两种空间复杂度为O(1)的解法:
- 解法2:排序,然后看这个数字和上一个数字是不是一样。时间复杂度为O(nlogn),空间复杂度为O(1)
- 解法3:鸽巢原理(原地置换、原地哈希)。因为这个题目比较特殊,长度为n而且所有数字都在0~n-1里面,所以如果没有重复的数字的话,这个数组排序之后每个元素和其下标是一一对应的(元素值==下标值)。如果有重复的元素的话,有的元素无法再放在其值下标的位置的,因为那里面已经有了一个相同值的元素了。
- 如果当前这个元素值不等于其下标
- 如果其值对应下标不等于其值,则交换(则这个值可以放到相应下标的位置里了)
- 如果其值对应下标等于其值,找到重复元素
- 如果当前这个元素值等于其下标,则判断下一个下标的元素
- 如果当前这个元素值不等于其下标
- 三种解法的效果,其中第一行为解法三,第二行为解法2,第三行为解法1的map实现,第四行为解法1的布尔数组实现。
代码
解法1
- 发现直接用一个O(n)的布尔数组(下),比用map(上)的效果更好,所以最好还是少用STL(盲猜?)
- 容器访问最好直接用下标访问,有些情况会明显下标更快
class Solution {
bool a[100005];
public:
int findRepeatNumber(vector<int>& nums) {
memset(a,0,sizeof(a));
int ret=0;
// 最好是直接下标访问,一些情况会明显比用迭代器快
// 我写这个代码的时候不知道hhh
for(vector<int>::iterator it=nums.begin(); it!=nums.end(); it++){
if(a[*it]){
printf("%d
",*it);
ret = *it;
break;
}
else{
a[*it]=true;
}
}
return ret;
}
};
解法2
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
sort(nums.begin(),nums.end());
int pre = nums[0];
for(int i=1;i<nums.size();i++){
if(nums[i]==pre) return pre;
pre = nums[i];
}
return 0;
}
};
解法4
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
for(int i=0;i<nums.size();i++)
{
while(i!=nums[i]){
if(nums[nums[i]]==nums[i]){
return nums[i];
}
else{
swap(nums[i], nums[nums[i]]);
}
}
}
return 0;
}
};