下面有彩蛋
题目描述
给定一个长度为n + 1的整数数组nums
,数组中所有的数字都在1~n范围内,其中
n
≥
1
n \geq 1
n≥1。请找出数组中任意一个重复的数字,但不能修改输入的数组。
样例
Input: nums = [2,3,5,4,3,2,6,7]
Output: 2或3
解题思路
-
普通思路
-
描述
遍历整数数组,一一对比,相等则返回。 -
实现代码:
/* 使用到的头文件 #include <vector> */ int duplicateInArray(vector<int> &nums) //传入数组,返回重复的数字 { for (int i = 0; i < nums.size(); i++) //二重循环遍历 { for (int j = i + 1; j < nums.size(); j++) { if (nums[i] == nums[j]) //相等返回该值 { return nums[i]; } } } }
-
复杂度分析
时间复杂度: O ( n 2 ) O(n^2) O(n2)
二重循环需要 n 2 n^2 n2
空间复杂度: O ( 2 ) ≈ O ( 1 ) O(2) \approx O(1) O(2)≈O(1)
使用两个临时变量i,j
-
-
高级思路
-
描述
本题目存在一个特性:数组长度为n + 1,数组数字范围1 ~ n,那么一定存在重复的数(抽屉原理)。
那么我们可以这样做:将数字范围一分为二,一者数字范围为1 ~ n/2,另一者则是 n/2 + 1 ~ n。将数组数字分派给这两个范围,一定存在其中一个数字范围中数组个数大于 n/2,然后将这个数字范围一分为二,递归下去,就可以找到。
-
实现代码
/* 使用到的头文件 #include <vector> */ int duplicateInArray(vector<int> &nums) { int l = 1, r = nums.size() - 1; //以1-n作为二分边界 int mid,sum; while (l < r) //循环条件 { mid = l + r >> 1; //取中,二分范围为[l,mid] [mid + 1,r] sum = 0; //统计处于[1,mid]范围内数的个数 for (int i = 0; i < nums.size(); i++) { sum += (nums[i] >= l) && (nums[i] <= mid); } if (sum > (mid - l + 1)) //如果数的个数大于[l,mid]区间表示个数,取[l,mid] { r = mid; } else //否则取[mid + 1,r] { l = mid + 1; } } return r; //返回找到的数 }
-
复杂度分析
时间复杂度: O ( l o g n ) O(log^n) O(logn)
就是一次二分查找
空间复杂度:$O(4) \approx O(1) $
使用四个临时变量l,r,mid,sum
-
二分查找模板
int binarySearch(int l,int r)
{
while(l < r)
{
mid = l + r >> 1; //取中
if(check(mid)) //中值大于等于查找值,取区间[l,mid]
{
r = mid;
}
else //中值小于于查找值,取区间[mid + 1,r]
{
l = mid + 1;
}
}
if(equal(l)) //l对应值等于查找值,返回下标
{
return l;
}
else //返回-1表示找不到
{
return -1;
}
}