【每天学习一点算法 2026/04/30】寻找重复数

每天学习一点算法 2026/04/30

题目:寻找重复数

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

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

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

作者:LeetCode

链接:https://leetcode.cn/leetbook/read/top-interview-questions-hard/xwz4lj/

来源:力扣(LeetCode)

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  1. 二分查找

    首先我们来分析一下,假设我们的 m 是重复数

    1 ~ m-1 范围中的数都是唯一的,那么这个范围中的任意一个数 i 一定满足:小于等于 i 的数字个数 count 是小于等于 i 的。

    m ~ n 范围中的数因为存在重复数 m,那么这个范围中的任意一个数 i 一定满足:小于等于 i 的数字个数 count 是大于 i 的。

    有点抽象我们以 nums = [3,1,3,4,2] 为例:

    • m = 3

    • 1 ~ 2 范围内有 1 和 2 两个数他们 count 分别是 1 和 2,满足上面的规律。

    • 3 ~ 4 范围内有 3 和 4 两个数他们 count 分别是 4 和 5,满足上面的规律。

    那么我们就可以根据这个规律在 1 ~ n 范围进行二分查找,找到第一个 count 大于它自身的数字就是重复数字。

    typescript 复制代码
    function findDuplicate(nums: number[]): number {
      let left = 1, right = nums.length - 1
      let res = 0
      while (left <= right) {
        const mid = Math.floor((left + right) / 2)
        let count = 0
        for (let i = 0; i < nums.length; i++) {
          if (nums[i] <= mid) count++
        }
    
        if (count > mid) {
          right = mid - 1
          res = mid
        } else {
          left = mid + 1
        }
      }
    
      return res
    };
  2. 快慢指针

    我们如果根据数组建立一个链表,使他的下一个节点在数组中的索引等于当前元素值。

    nums = [1,3,4,2,2] 为例:

    • 第一个节点值为:1
    • 第二个节点值为:下标为 1 的 3
    • 第三个节点值为:下标为 3 的 2
    • 第四个节点值为:下标为 2 的 4
    • 第五个节点值为:下标为 4 的 2

    此时出现了重复的节点表示这个链表是有环的,只有存在有重复数字这样生成的链表一定就会有环,而环的入口 2 就是重复的数字。

    这个问题就变成了寻找链表环的入口的问题,我们可以使用快慢指针,快指针每次移动两步,慢指针每次移动一步,根据 Floyd 判圈算法 两个指针一定会相遇。

    假设环的长度是 len,从起点到环的入口步数是 a,入口到相遇位置的步数是 b,从相遇位置回到入口的步数是 c。

    首先 len = b + c

    两指针相遇时,慢指针走了 a + b 步,快指针走了 2(a + b) 步。

    因为两指针相遇时,快指针一定会比慢指针多走若干圈,假设时 x 圈

    那么快指针走的步数也可以表示成:a + b + x * len

    于是就有了: 2(a + b) = a + b + x * len

    a = x * len - b = (x - 1) * len + c

    根据这个公式:如果两个指针一个从快指针出发一个从相遇点出发,当一个指针走了 a 步到达起点时,另外一个指针刚好也能绕 x - 1 圈后再走 c 步回到入口。

    所以我们需要先利用龟兔赛跑式快慢指针到达相遇点,再移动一个指针至起始点,然后两个指针每次都只移动一步,相遇的节点就是环的入口也就是我们要找的重复数。

    typescript 复制代码
    function findDuplicate(nums: number[]): number {
      let fast = 0, slow = 0
      // 利用 Floyd 判圈移动指针到相遇点
      do {
        fast = nums[nums[fast]]
        slow = nums[slow]
      } while (fast != slow)
    	
      slow = 0 // 将慢指针移动到起始点
      // 同步移动快慢指针直至相遇
      while (fast != slow) {
        slow = nums[slow]
        fast = nums[fast]
      }
      return fast // 返回相遇点指针
    };

    题目来源:力扣(LeetCode)

相关推荐
QuZero7 小时前
ReentrantReadWriteLock mechanism
java·后端·算法
Morwit7 小时前
【力扣hot100】 494. 目标和
数据结构·算法·leetcode
handler017 小时前
算法:图的基本概念
c语言·开发语言·c++·笔记·算法·图论
科研前沿7 小时前
像素即坐标・室外无边界:2026 最新无感定位技术,驱动数字孪生实景可控—— 镜像视界技术白皮书
大数据·人工智能·算法·重构·空间计算
YJlio7 小时前
《Windows Internals》10.5.1 ETW 概述:看懂 Windows 的“事件高速公路”
java·windows·笔记·stm32·嵌入式硬件·学习·eclipse
少许极端7 小时前
算法奇妙屋(五十)-二分与双指针的结合 + 2024秦皇岛-Problem D
算法·二分+双指针
love在水一方7 小时前
【Voxel-SLAM】 体素地图与Bundle Adjustment算法深度分析(四)
人工智能·算法·机器学习
木木_王7 小时前
嵌入式Linux学习 | 数据结构 (Day03)顺序表与单链表 超详细解析(含 C 语言实现 + 作业 + 避坑指南)
linux·c语言·数据结构·学习
阿Y加油吧7 小时前
二刷 LeetCode:198. 打家劫舍 & 279. 完全平方数 复盘笔记
笔记·算法·leetcode