【题解报告3】寻找重复数(小白作者详细讲解快慢指针)(勿喷)

一、题目链接

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 = [3,3,3,3,3]
输出:3
提示:
1 <= n <= 105
nums.length == n + 1
1 <= nums[i] <= n
nums 中 只有一个整数 出现 两次或多次 ,其余整数均只出现 一次

三、涉及知识点

四、算法分析

很容易想到的就是
1.枚举,双重循环去枚举就行了
2.排序,排序好后两个重复的数相邻
3.哈希,记录每个整数出现的次数来寻找

本文主要介绍一下编者小白自己去学习和思考学会的用双指针来做这道题.

1.算法思路:

  1. 先把整型数组抽象成一个链表(因为元素范围为[1,N]而数组下标为[0,N]且只有一个数字是重复的)
    将数组的每个元素构成一个节点,每个元素对应的下标作为这个节点的地址,每个元素其本身的值作为每个节点的指针域,其值为另一个节点的地址,指向下一个相对应的节点.如下图:

  2. 因为数组中有两个重复数字即有两个节点的指针是相等的指向同一个节点,所以所构成的链表是有环的,然后我们只需要找到两个节点的指针都指向的同一个节点时的该节点的地址(也就是那两个相等的指针的值,这个值也就是要找的重复的数字了)

  3. 怎么找这个地址(重复数字)呢?
    我们定义两个’指针’,慢指针s和快指针f,慢指针每次走一步,一开始两个指针都指向第一个节点,快指针每次走两步,当慢指针和快指针相逢的时候,我们让让其中一个指针重新值向第一个节点,然后让这两个指针每次都走一步,当这两个指针再次相逢的时候,这两个指针所指向的节点就是环的入口节点,这两个指针所保存的值就是我们要找的数.

2.为什么(自己浅略的理解而已)

  1. 为什么快慢指针的速度要分别为2和1,3和1不行吗
    假设两个指针都全部进入环内以后,(只往一个方向)两个指针的初始相距距离为N,则快慢指针速度分别为2和1时,两个指针的相对速度为1,它们之间的距离会一次次减少1,N每次减少1,N一定会减少到0,也就是这两个指针一定会相逢.
    而当快慢指针速度分别为3和1时,两个指针的相对速度为2,当两个指针都进入环内时,初始距离为5时,每次两个指针的距离减2,然后它们之间的距离变化为,5-2=3,3-2=1,1-2=-1(在环内快指针套了慢指针一圈),它们就错过了没有相逢,一些情况下,这两个指针在环内可能永远不相逢.
  2. 为什么快慢指针相逢后,让其中一个指针重新指向第一个节点,然后让两个指针每次都只走一步,当这两个指针再次相逢的时候,这两个指针所指向的节点就是环的入口节点呢?
    在这里插入图片描述

首先将链表分段,然后设环的入口节点为a点,设从第一个节点到环的入口节点的距离为L,快慢指针在第t次移动后相遇的节点记为b点(第一次相遇),从环的入口a点到b点的距离记为x,从b继续前行,到达环的入口节点a的这段距离记为y.则快慢指针相逢时,有以下关系:
1)慢指针走过的距离为:t = L+x
2)快指针走过的距离为:2t = L+x+n*(x+y),n为两个指针相遇前快指针在环内循环的圈数,n>=1.
联立上面两个式子得到:L=n*(x+y)-x,然后两边再减y得 L-y=(n-1)(x+y),然后
L=y+(n-1)
(x+y),x+y即为环的长度,所以从链表第一个结点到环入口节点a的距离等于从b点向前走到环入口节点a的距离加上(n-1)个环的长度。此时将快指针重新指向第一个节点,慢指针不动还指向b点,然后它们同速移动每次都只走一步,快指针走了L步到达环入口节点a,然后L==y+(n-1)*(x+y)慢指针从b点而经过(n-1)个周期又会到b点然后慢指针还有y步要走,而从b点往前走y步到达的就是环入口节点,所以此时快慢指针就在环的入口节点a点相遇(第二次相遇).

五、源码讲解

int findDuplicate(int* nums, int numsSize) {
    int slow=0,fast=0;//(1)
    do{
        slow = nums[slow];//(2)
        fast = nums[nums[fast]];//(3)
    }while(slow!=fast);
    fast =0 ;//(4)
    while(fast!=slow){//(5)
        fast = nums[fast];
        slow = nums[slow];
    }
    return slow;//(6)
}

(1)定义两个指针,初始化都为0,指向第一个节点(第一个节点的地址为0).
(2)通过一个节点的指针域(即数组中相对应元素的值),使慢指针指向下一个节点,赋予慢指针等于下一个节点的地址
(3)快指针每次走两步,通过当前节点的指针域得到下一个节点的地址,再用下一个节点的地址找到下一个节点的指针域,也就是下下个节点的地址
(4)快慢指针第一次相遇后,让快指针重新指向第一个节点,即让它保存的值为第一个节点的地址
(5)快慢指针都一步一步地走直到再次相遇
(6)返回此时快慢指针的值,即重复的数字

六、FAQ

有什么问题欢迎指出!

  • 14
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值