[LeetCode] 287. Find the Duplicate Number

本文介绍了一种寻找数组中重复数字的算法,提供了两种高效的方法:二分查找和环路起点检测,并详细解释了它们的时间复杂度和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      [LeetCode] 287. Find the Duplicate Number(Hard)

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

Note:
1.You must not modify the array (assume the array is read only).
2.You must use only constant, O(1) extra space.
3.Your runtime complexity should be less than O(n2).
4.There is only one duplicate number in the array, but it could be repeated more than once.

不考虑约束条件,常规做法有以下三种:
1.暴力 time- O(n2) space- O(1)
遍历数组,用一个变量存储当前数字,再来一层遍历,找到相同数字即可。
2.哈希表 time- O(n) space- O(n)
遍历数组时,用当前数字作为哈希表数组的下标,该值改为真,若已存在于表中,即找到。
3.排序法 time- O(nlogn) space- O(1)
先对数组排序,接着遍历,遇到前后数字相同即找到。时间空间都满足,但是要修改数组不符合要求。

可行算法有以下两种:
1.二分查找 time- O(nlogn) space- O(1)
对于这n+1个数,如果重复数字在1~ 1+n2 范围内,即整个数组内大于搜索值域均值 1+n2 的数的个数大于 n2 ,接下来缩短搜值域范围可缩小至1~ 1+n2 ,迭代下去,即可得到所求数字。

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int s,e,count,mid,i;
        for(s=0,e=nums.size()-1;s<=e;count>mid?e=mid-1:s=mid+1){
           count=0;
           mid=s+(e-s)/2;
           for(i=0;i<nums.size();i++)
             if(nums[i]<=mid)
               count++;
         }
        return s;
    }
};

2.环路起点检测 time- O(n) space- O(1)
从数组首位开始,将值作为数组下标映射到数组下一值,这样,由于数组值中有一重复数字,所以在重复值出现时会陷入循环。根据Floyd’s cycle-finding algorithm,使用快慢两个指针,在第一次搜索时快指针每轮映射两次,而慢指针一次,这样当快慢指针相等时,即发现环路,但此时指针数值不一定是所求重复数字。这时将快指针置零,从数组首位开始遍历,且变为每轮映射一次,直到与原来的慢指针相遇,此时就是所求重复数字。关于时间复杂度的推算,是第一次发现环路的n加上第二次找到重复数字的n,可得。
P.S.这种方法使用按理说应该有一个前提条件的,那就是原始数组里不能有nums[n]=n的情况,否则会陷入自身死循环,可能得到错误答案。题干里并没有给出这个说明,但是从测试全部通过来看这个条件是隐含的。

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int slow=0,fast=0;
        do{
            slow=nums[slow];
            fast=nums[nums[fast]];
        } while(slow!=fast);
       fast = 0;
        while(fast!=slow){
            slow=nums[slow];
            fast=nums[fast];
        }
        return fast;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值