剑指 Offer 03. 数组中重复的数字
1. 题目描述
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
输入:[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
限制:
2 <= n <= 100000
2. 解题思路
因为n个数并且范围落在0-n-1这个区间内,假设如果没有重复的数字,那么应该刚好有0,1,2,···n-1个数落在0-n-1这个数组下标区间内,如果对其进行排序,应该数组元素的值刚好对应数组下标的值。由于本题明确说明了在其他条件不变的情况下,有些数字是重复的,在此条件下找到重复的元素。
所以第一考虑排序,然后得到重复的元素,但排序的时间复杂度过高,并且排序过后找到重复元素,其实也可以采用二分查找,如果发现,元素比元素下标要小,那么左边一定有重复元素,而如果元素与元素下标相等,那么左右两边都可能会有重复元素,而大于的情况不可能; 但是这样的二分查找运气好可以确定一个比较小的区间,运气不好需要不断挑选判断点。而在要求只找到一个重复元素的情况下,是否可以考虑在排序的过程中,利用元素下标相等来排除不可能重复的元素,或者说在排序中,划分哪些排好序的元素不重复,哪些尚未排序的可能有重复元素。
因此我们考虑不断置换元素,使元素落在与数组下标相等的位置,这样,有点类似在数组内形成了一个map映射。这样不断置换元素,当发现某个需要置换的元素,其数组下标位置已经有了元素值得时候,便得到了结果。
其实这种思路本质上就是利用map映射。
3. 解题思路
// 使用map映射解决问题,从头到尾遍历,将nums[i]的值作为键,i作为值存入mp中,,一旦发现有nums[i]与mp中的键相同,那么便找到了一个重复点元素
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
map<int,int> mp;
map<int,int>::iterator iter;
for(int i=0;i<nums.size();i++){
iter=mp.find(nums[i]);
if(iter!=mp.end()){
// cout<<nums[i]<<endl;
// cout<<"Find, the key is"<<iter->first<<endl;
// cout<<"Find, the value is"<<iter->second<<endl;
return nums[i];
}else{
mp[nums[i]]=i; //键 值对,find寻找键
// cout<<nums[i]<<endl;
}
}
return -1;
}
};
//利用数组本身的特性,构建一个在数组内的map映射,来解决问题
class Solution {
public:
int findRepeatNumber(vector<int>& nums) {
int i=0;
int length=nums.size();
while(i<length-1){
if(i==nums[i]){
i++;
}else{
if(nums[i]==nums[nums[i]]){
return nums[i];
}
swap(nums[i],nums[nums[i]]);
}
}
return nums[i];
}
};