LeetCode - Find the Duplicate Number

题目

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
Note:

  • You must not modify the array (assume the array is read only).
  • You must use only constant, O(1) extra space.
  • Your runtime complexity should be less than O(n2).
  • There is only one duplicate number in the array, but it could be repeated more than once.

分析

这个题是两部分问题,第一部分的证明看起来比较直接,在数学上可以通过反证法证明。

第二部分是说现在有 n+1 个位置,放进去的数字都是从 1 到 n 的,但是存在一个重复的数字,其他的都不重复,让用 O(1) 的空间复杂度和少于O(n2)的时间复杂度找出这个重复的数字。示例输入输出可能是

[2,3,1,2] -> 2
[2,2,1,2,2] -> 2

一个比较详细的解答可以参考
https://segmentfault.com/a/1190000003817671

这里我想解释一下链接中的最后一个方法:映射找环法。代码可以参考上述链接,这里只是将其原理进行一个详细的论述

可以先看一下上述链接中的解释,这个算法的思路如下:

将数组中每个元素都看成链表中的一个节点,链表节点有两个基本要素就是节点的值(val)和指向下一个节点的指针(next),我们假设这个数组表示成 array[], 某个节点的索引是 index,在这个算法所想象的链表中:

  • 节点的值 val 就是数组在这个位置的值 array[index]
  • 节点的指向想一个节点的指针 next 也是数组在这个位置的值 array[index]

举个例子,数组 [2,3,1,2] 构成的链表如下图所示

这里写图片描述

我们发现这个图中出现了一个环,下面我们说明在给定的条件下,从数组的第0个元素出发,一定会进入一个环中,且这个环包含了重复的那个数字:

  1. 假设从第0个元素出发,最后没进入环中(反证),这就说明由0出发的链表的尾节点的next是null
  2. 然而给定条件下,数组中每个元素的值都是1 - n之间的数字(同时根据之前的构造方法数组的值也就是其next指针),说明这个链表中不会出现指向null的节点

上面的1和2相互矛盾,说明从数组的第0个元素出发,一定会进入一个环中

我们知道,指向环的入口元素(上例中的array[2]=1)的指针值(next)相同,而根据构next又等于节点的val,说明换入口元素的上一个节点就是重复的那个值。

事实上构造出来的图中可能存在多个环,但由于题的条件限定了只可能有一个重复元素,所以其中只有一个环会带着一个或多个“尾巴”,这个“尾巴”是进入环之前的部分。这个题的巧妙之处就在于数组中元素都在1-n之间,说明不会有节点的next值等于0,即array[0]一定是这样的一个“尾巴”的头部,而不会出现在环的内部。

这样原问题就转化为了在单链表中找环入口的问题(重复的数字是环入口前一个节点),利用快慢指针的办法可以很容易的写出代码(网上有很多资料,也可以看上文链接中的代码)

总结一下上面的几个关键点

  • 将数组看成一个链表,链表的 val 和 next 都是数组元素值
  • 从第0个元素出发,必然会形成环
  • 重复的数字节点在环的入口的前一个节点处

最后我想说的是,用数组表示链表或树状也是一种常见的办法,效率很高,以前学数据结构的时候学到过但后来一直没用过,可以看看 java 的 TImer 类的源代码中最小堆是如何实现的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值