【每天学习一点算法 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)

相关推荐
辰海Coding8 小时前
MiniSpring框架学习笔记-解决循环依赖的简化IoC容器
笔记·学习
晓梦林8 小时前
cp520靶场学习笔记
android·笔记·学习
心中有国也有家9 小时前
cann-recipes-infer:昇腾 NPU 推理的“菜谱集合”
经验分享·笔记·学习·算法
Upsy-Daisy9 小时前
AI Agent 项目学习笔记(八):Tool Calling 工具调用机制总览
人工智能·笔记·学习
绝知此事9 小时前
【算法突围 01】线性结构与哈希表:后端开发的收纳术
java·数据结构·算法·面试·jdk·散列表
碧海银沙音频科技研究院10 小时前
通话AEC与语音识别AEC的软硬回采链路
深度学习·算法·语音识别
csdn_aspnet10 小时前
Python 算法快闪 LeetCode 编号 70 - 爬楼梯
python·算法·leetcode·职场和发展
LuminousCPP11 小时前
数据结构 - 线性表第四篇:C 语言通讯录优化升级全记录(踩坑 + 思考)
c语言·开发语言·数据结构·经验分享·笔记·学习
魔法阵维护师11 小时前
从零开发游戏需要学习的c#模块,第十四章(保存和加载)
学习·游戏·c#