287. 寻找重复数

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。

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

示例 1:

输入:nums = [1,3,4,2,2]输出:2

示例 2:

输入:nums = [3,1,3,4,2]输出:3

示例 3:

输入:nums = [1,1]输出:1

示例 4:

输入:nums = [1,1,2]输出:1

思路:

快慢指针法

这一题可以用快慢指针来做,并且就和找环形链表如环处(链接)一模一样的思路。

好吧,这一题能用快慢指针,并且能转换成环形链表题这一事实多少有点抽象。但仔细分析下来就也还可以接受:

为什么可以扯到“链表”?因为我们这里虽然没有链表,但是我们可以通过数组的元素去访问对应的下标,以此来模拟链表访问下一个节点的操作。

为什么可以出现环?因为题目中给的数组有n+1个元素,且都分布在1到n之间,且至少有一个重复,一旦有重复,则说明我们从这两个地方的值都可以来到同一个下标——当一个地方可以有两个来源,不就相当于环形链表的入环处了吗?

所以题目中让我们找到这个重复元素,其实就等于找环形链表的入环处,思路也完全和之前一致——套用我们的结论:快慢指针相遇后,将其中一个移动到开头,同时同速前进,再次相遇就是入环处,也就对应着这一题的重复元素。

按序归位法

感觉快慢指针对于这道题来说多少还是有点超前了,虽然理解后觉得挺巧妙的,但还是不好想。一般对于这种题,给定n+1个整数的数组,并且数字都在1到n之间的情况,我们可以用“按序归位”的思路:比如数组中的数字1,我们就把它放在下标0处,数字2放在下标1处……数字n放在下标n-1处。当然,因为这一题我们必然会有相同的两个数字,那么肯定会造成位置冲突放不下的问题,那么这也就是我们的突破口。

我们具体的写法就是,遍历我们的数组,假如出现当前数组值nums[i]!=nums[nums[i]-1]的情况 (比如我们举个例子,假设i=0,我们希望nums[0]=1,这样则满足nums[i]==nums[nums[i]-1],一旦不满足这个式子,我们的数值位置就没摆放正确),就证明需要我们找到正确的nump[i]值(即i+1)来放在num[i]位置上。但我们不能直接赋值,因为那样会破坏数组内部的组成。因此为了保留数组内部的数据,我们只能通过替换的方式,我们把此时nums[i]上错误摆放的数移动到它应该出现的地方nums[nums[i]-1],并且把那个地方的数字移到nums[i]上来。就通过这么简单的一次交换,我们起码把错误的数字摆放成功了,此时如果nums[i]也摆放正常则皆大欢喜;摆放不正常,我们就对新的错误摆放的数进行同样操作,直至nums[i]==nums[nums[i]-1]。

这样操作下来,所有的位置都会被正确摆放一遍,即下标0上有数字1,下标1上有数字2……下标n-1上有数字n。然而,因为我们有重复的数字出现,则至少有一个位置是配对不上的。比如我们共有4个数,本来是1 2 3 4,但因为我们数字范围只有1-3并且得有一个重复,所以假设四个数字变成了1 2 3 2,那么下标3就是不匹配的。

所以我们最后遍历一遍,找到第一个下标和值不匹配的元素,就是最终结果。

代码(快慢指针):

class Solution(object):def findDuplicate(self, nums):slow, fast = 0, 0while True:slow= nums[slow]#慢指针 走一步 直接按下标定位1次fast =  nums[nums[fast]]#快指针走两步 按下标定位之后,再当成下标再定位if slow == fast:#相遇时breakbreakslow = 0#将其中一个挪到初始处while slow != fast:#同时前进直到相遇slow, fast = nums[slow], nums[fast]return slow#相遇处就是入环处

代码(按序归位):

class Solution(object):def findDuplicate(self, nums):for i in range(len(nums)):while nums[i]!=nums[nums[i]-1]:#不能写成i!=nums[i]-1#把当前nums[i]上的错误值与它应该出现的位置值交换nums[nums[i]-1],nums[i]=nums[i],nums[nums[i]-1]'''上面这句话等同于下面这三句,效果一样temp = nums[nums[i]-1]nums[nums[i]-1]=nums[i]nums[i]=temp'''#但是不可以用这句话,因为这样会先给nums[i]赋值# nums[i],nums[nums[i]-1] = nums[nums[i]-1],nums[i]for i,n in enumerate(nums):#归位结束后重新遍历一波if i+1!=n:#出现不匹配时,就找到了return n

小结:

本来以为按序归位的方法会比双指针来的简单清晰一些,后来思路写着写着好像也有点复杂了……其实两个方法内在思想差不多,都是通过下标去前进搜索。对于第二个方法,如果把我给的注释去掉其实是非常简短的,还算清晰。其中也有一些容易出错得地方我也都写在注释里了,比如a,b=b,a这样的交换操作,其实也涉及到顺序的问题。

通过这一题,我们引出了对于“数字都在 1 到 n 之间的包含 n + 1 个整数的数组”的常用解决方式:按序归位。后面出现类似的条件也可以考虑这样操作。当然如果数字是在0到n-1之间的范围就更方便了,毕竟这一题我们一直有一个“下标0对应数字1”这样一个加一减一的对应,才显得代码比较别扭。

寻找重复数 快慢指针与按序归位相关推荐

  1. 快慢指针(LeetCode寻找重复数),弗洛伊德的乌龟和兔子

    写此篇博客在于总结,记忆之用,欢迎评论补充. 弗洛伊德的乌龟和兔子,即快慢指针. 对于LeetCode287题,寻找重复数,题目如下: 给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 ...

  2. [Leedcode][JAVA][第287题][寻找重复数][HashSet][二分查找][快慢指针]

    [问题描述][中等] 给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数.假设只有一个重复的整数,找出这个重复的数.输入: ...

  3. 【LeetCode笔记】287. 寻找重复数(Java、快慢指针、原地、链表)

    文章目录 题目描述 思路 & 代码 更新 题目描述 可以理解成数组版本的 环形链表 II 更多详细思路可见以上超链接. 思路 & 代码 如何转化成逻辑上的链表? nums[i] 是 第 ...

  4. 证明利用快慢指针寻找有环单链表中环的起点算法

    问题:给定一个有环单链表,找到链表中环的起点,也就是说,找到下图中的单链表中Join点: (本图来源于http://www.cnblogs.com/xudong-bupt/p/3667729.html ...

  5. 双指针算法之快慢指针(二):力扣【寻找链表的第N个点】leetcode-876、19

    双指针算法之快慢指针(二):力扣[寻找链表的第N个点]leetcode-876.19 看完本文,可以去解决力扣的 867 题和 19 题 以往参考:双指针算法之快慢指针(一):力扣[判断链表是否有环] ...

  6. 快慢指针追逐法寻找单链表中环的起点

    寻找单链表环的起点 题目 解决思路 代码 说明 题目 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null.如下图中数字2对应的节点为环的第一个节点: 输入:head = [3 ...

  7. 快慢指针寻找循环节点

    循环链表找循环开始的节点: //设置快慢指针 快指针一次走两个 慢指针一次走一个 第一次相遇后将快指针重新放到头部 //快指针一次走一个 慢指针一次走一个 第二次相遇的位置就是环开始的位置 题目 14 ...

  8. 双指针算法之快慢指针(一):力扣【判断链表是否有环】leetcode-141、142

    一.简介:什么是快慢指针? 快慢指针,顾名思义,无非就是设置一个快指针,一个慢指针,初始化的时候,快指针和慢指针都指向链表的头结点,前进的时候一个在前一个在后,结合起来可以十分巧妙的解决链表中的一些问 ...

  9. Faster\Slower 快慢指针的应用

    leetcode很多题目都是利用快慢指针来解决题目,下面具体讲解下快慢指针. 概念: 快指针在每一步走的步长要比慢指针一步走的步长要多.快指针通常的步速是慢指针的2倍.在循环中的指针移动通常为:fas ...

  10. 快慢指针的概念及其应用

    概念 快慢指针是指移动的步长,即每次向前移动速度的快慢.(例如,让快指针每次沿链表向前移动2,慢指针每次向前移动1) 应用 1.判断单链表是否存在环.如果快指针到达NULL,说明链表以NULL结尾,不 ...

最新文章

  1. 手把手教你如何新建scrapy爬虫框架的第一个项目(下)
  2. 如何设计一个深度学习项目
  3. ios 边录音边放_关于Android和iOS系统OneNote支持边录音边记笔记的需求和建议
  4. python 数据分析班_Python数据分析班
  5. PHP为什么以及什么时候使用单例模式?
  6. Linux chroot命令
  7. git拉取远程分支并创建本地分支和Git中从远程的分支获取最新的版本到本地
  8. Javascript:Promise异步编程解决方案
  9. 从零开始Android游戏编程(第二版) 第十章 游戏循环的设计
  10. 再也不见,Itchat!
  11. 我孩子的毛毯教会了我关于技术和古希腊人的知识
  12. 年终总结系列1:基于IFRS9的预期损失准备金
  13. Unreal 蓝图工具 spline 道路组件
  14. 快速学会linux上的chrony服务
  15. iqoo手机可以刷鸿蒙系统吗,华为老机型可以更新鸿蒙系统么-华为哪些老机型支持鸿蒙系统...
  16. 2018 年最流行的十大编程语言,Java 无可争议!
  17. 如何进行js的debug
  18. U8备份提示“远程组件初始化失败”
  19. ibm服务器日志文件提取,IBM服务器日志搜集方法
  20. 综合客运枢纽智能管理系统方案(可下载)

热门文章

  1. QT通过预定义宏输出pro文件内配置的变量
  2. 申奥动画长片名单出炉,动画质量大PK
  3. SSL 发布 SubGen 插件
  4. Python-类02(乌龟吃鱼游戏)
  5. 米勒奥尔模型中现金返回线,最佳持有量的推导
  6. 4515DO-DS3BK004DP压差传感器MEAS
  7. 霸榜MS MARCO!基于Transformer的混合列表感知排序模型
  8. kettle下载百度网盘地址含入门教程3(表间数据自定义传输)
  9. 论文阅读:(ECCV 2022) Towards Efficient and Scale-Robust Ultra-High-Definition Image Demoireing
  10. CentOS7 linux kill 命令