LeetCode 287 寻找重复数字

题目

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,返回 这个重复的数

你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。

示例 1:

复制代码
输入:nums = [1,3,4,2,2]
输出:2

示例 2:

复制代码
输入:nums = [3,1,3,4,2]
输出:3

示例 3 :

复制代码
输入:nums = [3,3,3,3,3]
输出:3

思路解析

实际上最简单的做法就是一次遍历即可,用Set维护,看是否出现过,这样很容易写,但是要额外的空间花销,时间空间都是O(n)

还有更省空间的做法,就是我们把nums当作链表来维护

数组:nums = [1,3,4,2,2]

我们把:索引 0 -> nums[0] 索引 1 -> nums[1] ...

理解为:

0 → nums[0] 1 → nums[1]

即:

i -> nums[i]

那么这个数组就变成:

0 → 1 → 3 → 2 → 4 → 2 → 4 → 2 ...

你会发现:

数字 2 出现重复

图结构中就形成了一个环

那么就可用链表找循环入口思路解决了

第一步:找相遇点(判断是否有环)

慢指针一次走一步

快指针一次走两步

如果存在环:

fast == slow

必然会相遇


第二步:找环入口

数学证明结论:

相遇后,把 slow 放回起点

slow 和 fast 每次走一步

再次相遇的地方就是环入口

环入口 = 重复数字

为什么第二次相遇一定是入口?(核心数学推导)

设:

a = 起点到环入口距离 b = 环入口到相遇点距离 c = 环剩余部分

当第一次相遇时:

slow 走了 a + b fast 走了 2(a + b)

因为 fast 比 slow 多走一圈:

2(a + b) = a + b + k(b + c)

化简得:

a = k(b + c) - b

可得:

a = c + (k-1)(b+c)

说明:

从起点走 a 步

等价于从相遇点走 c 步

而 c 正是:

相遇点到环入口的距离

因此:

slow 从头走 fast 从相遇点走

他们一定在入口相遇

代码

java 复制代码
class Solution {
    public int findDuplicate(int[] nums) {

        // 慢指针:每次走一步
        int slow = 0;

        // 快指针:每次走两步
        int fast = 0;

        // 第一阶段:找到相遇点
        // do-while 是因为必须先走一步
        // 不能直接比较 0 == 0
        do {
            // slow 走一步
            slow = nums[slow];

            // fast 走两步
            fast = nums[nums[fast]];

        } while (fast != slow);  // 相遇说明进入环

        // 第二阶段:寻找环入口
        // 将 slow 放回起点
        slow = 0;

        // slow 和 fast 同速前进
        while (slow != fast) {
            slow = nums[slow];
            fast = nums[fast];
        }

        // 再次相遇的位置就是环入口
        // 也就是重复数字
        return slow;
    }
}
相关推荐
im_AMBER1 小时前
Leetcode 123 二叉树的层平均值 | 二叉树的右视图 | 二叉树的层序遍历
数据结构·学习·算法·leetcode·二叉树
We་ct1 小时前
LeetCode 100. 相同的树:两种解法(递归+迭代)详解
前端·算法·leetcode·链表·typescript
样例过了就是过了1 小时前
LeetCode热题100 轮转数组
数据结构·算法·leetcode
ShineWinsu1 小时前
对于stack和queue经典算法题目:155. 最小栈、JZ31 栈的压入、弹出序列和102. 二叉树的层序遍历的解析
数据结构·c++·算法·面试·力扣·笔试·牛客网
能源系统预测和优化研究2 小时前
【原创改进代码】考虑电动汽车移动储能特性的多区域电网功率波动平抑优化调控
大数据·算法·能源
_F_y2 小时前
两个数组的动态规划
算法·动态规划
每天要多喝水2 小时前
动态规划Day32:最长公共子序列
算法·动态规划
骇城迷影2 小时前
代码随想录:栈和队列篇
java·服务器·算法
2401_858286112 小时前
OS54.【Linux】System V 共享内存(3) “共享内存+管道“修bug记录
linux·运维·服务器·算法·bug