1、问题描述
给定一个包含n+1个整数的数组nums,其中,每个整数的取值范围为[1,n],根据鸽巢原理可知,至少存在一个重复的整数。假设只有一个重复的数,找出这个数。
2、解题思路
- 边界条件与特殊情况:(1)nums中整数的个数小于2;
- 问题分析:以nums=[1,3,4,2,3]为例,索引index的范围是在[0,n],元素值value的范围是[1,n],想象一下,把数组看成一个链表,数组中每一个位置上的元素值作为下一个元素的索引,当有元素重复时,会得到下面这样的带有环的索引链表:
- 思路1:根据上面的分析,类比判断链表中是否存在环问题的解题思路,采用快慢指针来解决这个问题,整个思路分为两个步骤:
- (1)检测环。具体地,快、慢指针fast、slow都从0开始,进行如下循环:slow每次进行一跳,而fast进行两跳,比如,一开始slow=i、fast=i,下一步中,slow=nums[i],而fast=nums[nums[i]]。当slow等于fast,循环停止,此时slow和fast都停在环上某个节点。
- (2)找到环的入口。具体地,finder指针从0开始,slow从上一步中slow和fast在环中的相遇的开始,进行如下循环:slow和finder指针每次都进行一跳。当finder等于fast时,循环停止,此时finder指向的为环的入口。
- 这里可能有点难理解的是:为什么finder和slow会在环的入口处相遇?我们可以证明一下:
- 假设起点到环的入口处长度为m,环的周长为c,在检测环的过程中,快慢指针在环中相遇,此时,fast走了2n步,slow走了n步,根据以上假设可得出以下结论:
- 由于两个指针在环中相遇,所以多走的n步一定是c的整数倍,即n%c=0;
- fast和slow相遇时,slow在环中行走的距离是n-m,此时,若finder从起点开始,slow从相遇点开始,两者同步前进,每次一步,当finder走了m步到达环的入口处时,slow走了n-m+m=n步,而n又是c的整数倍,故两者会在环的入口相遇。
- 先声明一下数据结构:
- slow慢指针、fast快指针,finder指针。
- 初始化,一开始slow和fast都为0,finder也为0
- 处理逻辑: 检测环和找到环的入口。
- 这个方法的时间复杂度为O(n)O(n)O(n),空间复杂度为O(1)O(1)O(1)。
- 思路2:一说到查找,且时间复杂度小于O(n2)O(n^2)O(n2),则可以尝试二分查找,二分查找的思路如下:
- 由于数组的元素的取值范围在[1&#xff0c;n]&#xff0c;所以需要查找的数的区间也left&#61;1、right&#61;n&#xff0c;取该区间的中位数mid&#xff0c;统计数组中小于等于mid的元素个数count&#xff0c;如果count<&#61;mid&#xff0c;则说明重复的整数在[mid&#43;1&#xff0c;right]区间&#xff0c;否则在[left&#xff0c;mid]之间&#xff0c;如果left
- 由于进行了lognlognlogn次查找&#xff0c;统计小于mid的元素个数count时间复杂度为O(n)O(n)O(n)&#xff0c;所以时间复杂度为O(nlogn)O(nlogn)O(nlogn)&#xff0c;空间复杂度为O(1)O(1)O(1)。
3、代码实现
快慢指针实现&#xff1a;
class Solution {
public:int findDuplicate(vector<int>& nums) {if (nums.size() < 2){return 0;}int slow&#61;0;int fast &#61; 0;int finder &#61; 0;while(true){slow &#61; nums[slow];fast &#61; nums[nums[fast]];if(slow &#61;&#61; fast){break;}}while(true){slow &#61; nums[slow];finder &#61; nums[finder];if(slow &#61;&#61; finder){break;}}return finder;}
};
二分搜索实现&#xff1a;
class Solution {
public:int findDuplicate(vector<int>& nums) {if (nums.size() < 2){return 0;}int left &#61; 1;int right &#61; nums.size() - 1;while(left < right){int mid &#61; (left &#43; right) /2;int count &#61; countNums(nums,mid);if(count <&#61; mid){left &#61; mid &#43; 1;}else{right &#61; mid;}}return left;}int countNums(vector<int>& nums,int e){int count &#61; 0;for(int i&#61;0;i<nums.size();i&#43;&#43;){if(nums[i] <&#61; e){count &#43;&#43;;}}return count;}
};