当前位置: 首页 > news >正文

十三 287. 寻找重复数

287. 寻找重复数https://leetcode.cn/problems/find-the-duplicate-number/

给定一个包含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

提示:

  • 1 <= n <= 105
  • nums.length == n + 1
  • 1 <= nums[i] <= n
  • nums只有一个整数出现两次或多次,其余整数均只出现一次

进阶:

  • 如何证明nums中至少存在一个重复的数字?
  • 你可以设计一个线性级时间复杂度O(n)的解决方案吗?

核心思想:把数组看成链表!


把数组看作:从索引 i 可以跳到索引 nums[i]

假设nums = [1, 3, 4, 2, 2],索引是 0,1,2,3,4

索引: 0 1 2 3 4 数值: 1 3 4 2 2 ↓ ↓ ↓ ↓ ↓ 指向: 1 3 4 2 2 (即下一个节点的索引)

从索引0开始遍历:

  • 在索引0,值是1 → 下一步去索引1

  • 在索引1,值是3 → 下一步去索引3

  • 在索引3,值是2 → 下一步去索引2

  • 在索引2,值是4 → 下一步去索引4

  • 在索引4,值是2 → 下一步去索引2

  • 在索引2,值是4 → 下一步去索引4

  • ...

发现2 → 4 → 2 → 4 → 2 → 4... 成环了!

为什么一定会成环?

  • 有 n+1 个位置,数值范围是 [1, n]

  • 从任意位置出发,每次跳到的位置都在 [1, n] 范围内(不会跳出)

  • 只有 n 个有效位置,但你要无限走下去,根据鸽巢原理,必然会重复访问某个位置

  • 一旦重复访问,就形成了环!

环的入口就是答案!

路径示意:0 → 1 → 3 → 2 → 4 → 2 → 4 → 2 → 4... ↑___________| 从0出发:0 → 1 → 3 → 2 → 4 → (进入环) 环的部分:2 ↔ 4 之间循环 注意:2 是环的入口,而 2 正好就是重复的数字!

既然变成了链表有环问题,就可以用经典的 Floyd 算法:

算法步骤

第一阶段:快慢指针找相遇点

  • 慢指针每次走1步:slow = nums[slow]

  • 快指针每次走2步:fast = nums[nums[fast]]

  • 最终它们会在环内某点相遇

第二阶段:找环的入口

  • 将慢指针放回起点(0)

  • 快指针保持在相遇点

  • 两个指针每次都走1步

  • 再次相遇的点就是环的入口(即答案)

为什么第二阶段能找入口?(数学证明)

设:

  • 起点到环入口距离:a

  • 环入口到相遇点距离:b

  • 相遇点回到环入口距离:c

慢指针走的距离:a + b快指针走的距离:a + b + n(b + c)(n是绕的圈数)

因为快指针速度是2倍:

plain

复制

2(a + b) = a + b + n(b + c) => a + b = n(b + c) => a = n(b + c) - b = (n-1)(b+c) + c

这意味着:从起点走 a 步 = 从相遇点走 c 步(绕若干圈后)

所以一个从起点走,一个从相遇点走,每次一步,相遇时就是入口!

class Solution { public int findDuplicate(int[] nums) { // 第一阶段:快慢指针找相遇点 int slow = nums[0]; int fast = nums[nums[0]]; while(slow != fast){ slow = nums[slow];// 慢指针走一步 fast = nums[nums[fast]]; // 快指针走两步 } // 第二阶段:找环的入口(即重复数) slow = 0;// 慢指针放回起点 while(slow != fast){ slow = nums[slow]; // 两个指针都走一步 fast = nums[fast]; } return slow;// 相遇点就是重复的数 } }
http://www.jsqmd.com/news/556474/

相关文章:

  • Buildah多平台容器构建终极指南:使用QEMU跨架构构建Docker镜像
  • Swift元编程终极指南:使用Sourcery自动生成UserDefaults偏好设置代码
  • SQL视图实战:5个真实业务场景下的数据视图应用案例(附代码)
  • 终极指南:如何利用nvim-tree.lua实现文件重命名全自动化方案
  • Qwen-Image-Edit参数详解:如何调整CFG值平衡指令遵循度与图像保真度
  • VasDolly多线程优化实战:应对海量渠道打包挑战
  • Buildah容器调试终极指南:10个实用技巧快速解决构建问题
  • 告别单文件编译:VSCode + MinGW多文件C++项目高效开发指南
  • fluent_edem流固耦合方面的教学或者代做或者代码二次开发,气液固三相耦合。 接口优化...
  • Hexo Butterfly主题终极页脚导航配置指南:10分钟打造专业网站内链结构
  • Node.js日志标准化终极指南:使用morgan构建团队统一日志规范
  • tunnelto终极指南:构建高性能本地服务全球访问的高效方案
  • Llama-3.2V-11B-cot一文详解:low_cpu_mem_usage对加载速度提升37%
  • caj2pdf高级功能:如何快速为CAJ转换PDF添加大纲和目录导航
  • TOPSIS算法实战:用Python给河流水质排个名,附完整代码与避坑指南
  • Swift Markdown扩展开发:如何实现自定义Inline Nodes和Block Containers
  • Phi-3-Mini-128K项目实战:从零搭建一个Java面试题库与智能答疑系统
  • 告别显卡驱动残留困扰:Display Driver Uninstaller的深度清理全解析
  • 终极指南:掌握Starlight文档导航自定义排序的7个高级技巧
  • 终极指南:如何在ComfyUI中轻松使用LTX-2 AI视频生成插件
  • 实战指南:如何用Python+Spacy快速搞定非结构化文本中的实体识别(附代码)
  • 单片机程序运行时间测量方法与优化实践
  • 计算机毕业设计springboot城市新能源车辆租赁换电管理系统 基于SpringBoot的城市电动出行租换电综合服务平台 Java技术驱动的城市绿色交通电池共享运营管理系统
  • GPT-Neo终极自动布局指南:如何轻松实现高效分布式训练
  • Vue+DataV+Echarts实战:从零搭建企业级数据可视化大屏(附完整代码)
  • 微信小程序集成通义千问:打造悬浮窗智能对话助手
  • 如何用Hypothesis测试框架提升Python开发效率:10个实用技巧
  • SpinningMomo终极指南:如何用专业工具提升《无限暖暖》摄影体验
  • 终极Star History数据格式指南:掌握JSON响应与API版本控制的完整教程
  • Zynq AXI DMA实战:从零配置S_AXIS_S2MM到M_AXIS_MM2S的完整数据流(Vivado 2023版)