给定一个长度为 n 的整数数组 nums
,数组中所有的数字都在 0∼n−1 的范围内。
数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。
请找出数组中任意一个重复的数字。
一:修改数组
代码:
class Solution {
public:
int duplicateInArray(vector<int>& nums) {
int n = nums.size();
for (auto x : nums)
if (x < 0 || x >= n)
return -1;
for (int i = 0; i < n; i ++ ) {
while(nums[nums[i]] != nums[i]) swap(nums[i], nums[nums[i]]);
if (nums[i] != i) return nums[i];
}
return -1;
}
};
核心思想:
1.因为所有数字的范围都在0 - n-1,先遍历整个数组把数值 i 分配到第 i 个数组,这样的话,如果有重复的数的话,等循环完毕后肯定会有一个 数值与数组的位置不对应
一个重要的问题:如何把数值 i 分配到下标 i 的数组,
循环:while(num[num[i]] != nums[i]) swap(num[i],num[num[i]])
要理解为什么是 mun[i] and num[num[i]]
像一个问题:如果num [ i ] == num[num[i]] 是不是说明数值 i 分配到下标 i 的数组
二:不修改数组
与第一种方法截然不同
中心思想:
因为数组中有n个数,但是数的取值范围为 1 - n
所以肯定有重复的数
所以我们只需要把这个数的取值范围分成两半
以n = 9为例
1 2 3 4 5 6 7 8 9
然后遍历一遍数组如果数组的值在1-4范围内
count++
遍历完成后:
如果count > 4说明数组中一定有在1-4范围内的数存在不止一次
所以缩小范围
1 2 3 4 (同理)
如果 count <= 4,说明重复出现的数字在另外半块
代码:
class Solution {
public:
int duplicateInArray(vector<int>& nums) {
int left = 1,right = nums.size()-1;//因为这里right指的是有序数123456789的最右
//而不是数组的最右
int mid = 0;
while(left < right)
{
mid = (left + right)/2;
int cout = 0;//计算数组里的数和前半段数目是否符合
for(int x:nums)//遍历
{
if(x>=left&&x<=mid)
cout++;
}
if(cout > mid-left+1) right = mid;
else left = mid +1;
}
return right;
}
};
以下代码还有一种写法:
for(int x:nums)//遍历
{
if(x>=left&&x<=mid)
cout++;
}
for(int x:nums)//遍历
{
cout+=x>=left&&x<=mid
}