LeetCode 287. Find the Duplicate Number

本文介绍了一种寻找数组中重复数字的方法,特别适用于数组长度比数值范围多一个元素的情况。提出了一种基于链表环路原理的解决方案,该方案仅使用常数级别的额外空间,并实现了O(n)的时间复杂度。

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

题目

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.

题意

给出一个数组nums,包含了n+1个取值为1~n整数,证明至少存在一个数重复。假设这里只有一个重复的数,把它找出来。
提示
1. 不能修改原数组(假设数组是只读的)
2. 只能使用常数O(1)额外的空间
3. 时间复杂度必须少于O(n^2)
4. 数组中至少会有一个重复的数,但是它可能重复多次。


分析

这道题不难,但是,题目对存储空间进行了严格的控制,并且要求时间复杂度为O(n^2)。
我一开始的想法比较简单,也能解决问题。也就是方法一,后面看到了另一种解法,感觉更高效,便也贴出来分析了,即方法二


方法一 O(n^2)

类似冒泡排序,用两个for循环,如果a[i] == a[j] ,则找到了重复的数。

其时间复杂度为O(n^2),空间复杂度为0。

int findDuplicate(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];
    }
}

LeetCode 上跑53个测试样例,耗时555ms 。


方法二 O(n)

看到了一个更好的解法。

首先。数组的值是在1~n,数组长度为n+1,这个是一个很特别的设定,因为,数组的值作为下标是合法的。

比如数组 [1,3,4,2,1]
1->3->2->4->1->3->2->4->1->…3->2->4->1->

因为有重复的数,所以一定存在闭环,而重复的数则是环的入口。

那么我们可以利用 Linked List Cycle的做法,用一个fast和一个slow,fast每次走两步,slow每次走一步,那么它们必定在闭环某个位置相遇。
然后,就需要找到闭环的入口了。使fast = 0,这次,fast和slow一样,每次走一步,当fast = slow的时候,它们就走到了入口

开始 : slow = 1 fast = 3
第一次 : slow = 3 fast = 4
第二次 : slow = 2 fast = 3
第三次 : slow = 4 fast = 4 相遇了

找闭环入口,即重复的数字
开始 : slow = 4 fast = 0
第一次 : slow = 1 fast = 1 找到入口

这样做的时间复杂度为O(n),空间复杂度度O(1)

int findDuplicate(vector<int>& nums) {
    if (nums.size()<1)
        return -1;
    int slow = nums[0], fast = nums[nums[0]];
    while (fast != slow) {
        cout << fast << " " << slow << endl;
        slow = nums[slow];
        fast = nums[nums[fast]];
    }
    cout << "stop: " << fast << " " << slow << endl;
    fast = 0;
    while (fast != slow)
    {
        cout << fast << " " << slow << endl;
        fast = nums[fast];
        slow = nums[slow];
    }
    return slow;
}

LeetCode 上跑53个测试样例,耗时12ms 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值