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:

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

题目标签:Array, Binary Search, Two Pointers

  题目给了我们一个nums array, 让我们找到其中的重复数字。因为这一题有4个条件,所以有难度。1. 要求我们不能改动array;2. 只能用O(1)空间;3. 时间要小于O(n^2);4. 只有一个重复的数字,但是它可以出现最少1次。

  方法1:O(logn * n)

    利用binary search。

    题目给的数字是在 [1, n] 之间, array 的size 是 n+1。所以肯定有一个数字会至少出现2次。

    分析一下:  

      如果n 是5,那么就会有1 2 3 4 5 一共5个数字的可能,而array size 是6,那么其中一个数字肯定会至少出现两次。

      如果没有重复的数字,小于等于1的数字 出现的次数 等于 1;

                小于等于2的数字 出现的次数 等于 2;

                  ... 同理3;4;5。

      如果有重复的数字,如果重复的是1,那么 小于等于1的数字 出现的次数 肯定大于1;

      基于这个理论,我们可以在1 2 3 4 5 选出一个 mid, 遍历array来count 小于等于mid 的数字个数 小于等于 它自己mid 还是 大于 mid?

          如果count 小于等于mid, 说明 1 到 mid 这些数字 没有重复项, 重复项在 右半边 mid 到n, 所以缩小到右半边继续搜索;

          如果count 大于mid, 说明  1 到 mid 这些数字中 有重复项,缩小到 左半边继续搜索。

Java Solution:

Runtime beats 28.13%

完成日期:09/13/2017

关键词:Array, Binary Search

关键点:如果从1到n中 有重复项,那么必然有一个数字出现的次数至少是2次

 class Solution
{
public int findDuplicate(int[] nums)
{
/* Solution 1: binary search */
int low = 1, high = nums.length - 1; while(low <= high)
{
int mid = low + (high - low) / 2;
int cnt = 0; for(int a: nums)
{
if(a <= mid)
cnt++;
} if(cnt <= mid) // meaning duplicate is on the right side
low = mid + 1;
else // if cnt > mid, meaning duplicate is on the left side
high = mid - 1; } return low;
}
}

参考资料:

http://bookshadow.com/weblog/2015/09/28/leetcode-find-duplicate-number/

  方法2:O(n),利用slow 和fast 双指针,这个方法太巧妙了,有好几个点比较难理解。

    利用slow 和 fast 指针,经历两次 相遇,来找到重复的数字。

    相遇1: slow 一次走一步;fast 一次走两步。

        当它们相遇的时候,让slow 保留在相遇的点上。

    相遇2:让fast 从起点开始走,一次走一步;slow 在上一次相遇的点上开始走,一次走一步。

        当它们相遇的时候,相遇的点就是重复的数字。

    

    如何让slow fast 走步呢,利用slow = nums[slow]; fast = nums[nums[fast]]. 把index 所指的数字值 替换index 来模仿走步数。

    

    来结合图片分析几个关键点:

    

    S 为起始点,O 为圆的入口,M1 为第一次相遇的地方,x 为S到O的长度, y 为 O到M1的长度, z 为 M1到O的长度。

    1. 第一次相遇是为了找到它们的相遇点, 第二次相遇是为了找到圆的入口 O。

    2. 路程会有一段path 接着和一个circle:

      举例子来看一下:

       0 1 2 4 6 7

      [4,6,2,,7,,3,5]

      n = 7, 数字都是 1 ~ 7 中的选择, array size = 8, 肯定会有一个数字,至少出现2次。

      按照我们的方法来走一下:0 -> 4 -> 7 -> 5 -> -> 6 -> 3 -> 1 -> 6 -> 3 -> 1 -> 6 -> 3 -> 1 ... 一直重复 1 6 3

      红色部分可以看作为S - O path; 绿色部分可以看作为 O点; 蓝色部分看作为 圆。

    3. 为什么会有一段path:

      因为数字只能是 1 到 n, 所以index 0 肯定只能走过一次, 那么肯定有一段path, path 长度得看情况。

    4. 为什么会有一个circle:

      因为index 除了0, 有1到n(而且唯一不重复); 数字也是1到n, 所以每一次 index 指向数字 时候, 那个数字肯定能成为新的和唯一的 index,一直循环。

    5. 为什么O 点一定是 重复的那个数字?

      我们来看之前的例子: index array 里,红色的两列是重复的数字。3 能走到 1, 5 也能走到1, 3和5 代表着两条路,所以当两条路汇合的地方,就是重复的数字1。

    6. 为什么x = z?

      换句话说,为什么第二次它们能够相遇。这应该是最麻烦的地方。

      方法1: 验证一下

        第一次相遇后: slow 走的路程 = x + y;fast 走的路程 = x + y + z + y;fast = x + 2y + z。

                  因为第一次是slow 走一步,fast 走两步,所以 2slow = fast。

               代入公式:2x + 2y = x + 2y + z

                       x = z

        方法2:我们想象一下,

          情况1:如果slow 和fast 都从O点出发,slow 一次走一步,fast 一次走两步,那么当它们相遇的时候,slow 走了一圈,fast 走了两圈,而且它们一定相遇在O点;(可以在图上画刻度,自己走走看)

          情况2:如果我们多加一段path, 让它们从S点出发,slow 一次走一步,fast 一次走两步, 那么它们什么时候能相遇呢? 情况1中它们相遇在O点,那么多加一段path(x 是path的长度),它们会在O点的基础上,提早x 的距离相遇,因为它们多走了一段x的path,所以fast 会在O之前就赶上slow。所以 x 肯定等于 z。

    7. 如果不太明白的话,可以画一个类似的图,再画上刻度,自己多走几次,感受一下。

    

Java Solution:

Runtime beats 51.39%

完成日期:09/14/2017

关键词:Array, Two Pointers

关键点:快慢指针相遇的点就是重复的数字

 class Solution
{
public int findDuplicate(int[] nums)
{
/* Solution 2: */
int slow = 0, fast = 0;
// fist meeting
do
{
slow = nums[slow]; // slow takes one step each time
fast = nums[nums[fast]]; // fast takes two steps each time }while(slow != fast); // second meeting
fast = 0; while(slow != fast)
{
slow = nums[slow]; // both slow and fast take one step each time
fast = nums[fast];
}
// when they meet, the place must be entry of circle, and must be duplicate
return slow;
}
}

参考资料:

http://blog.csdn.net/monkeyduck/article/details/50439840

           

LeetCode 题目列表 - LeetCode Questions List

LeetCode 287. Find the Duplicate Number (找到重复的数字)的更多相关文章

  1. LeetCode 287. Find the Duplicate Number (python 判断环,时间复杂度O(n))

    LeetCode 287. Find the Duplicate Number 暴力解法 时间 O(nlog(n)),空间O(n),按题目中Note"只用O(1)的空间",照理是过 ...

  2. [LeetCode] 287. Find the Duplicate Number 寻找重复数

    Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), pro ...

  3. [LeetCode] 287. Find the Duplicate Number 解题思路

    Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), pro ...

  4. [LeetCode] 287. Find the Duplicate Number(Floyd判圈算法)

    传送门 Description Given an array nums containing n + 1 integers where each integer is between 1 and n  ...

  5. LeetCode : 287. Find the Duplicate Number

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAACRAAAAMMCAYAAAAhQhmZAAAMFGlDQ1BJQ0MgUHJvZmlsZQAASImVlw ...

  6. leetcode 217. Contains Duplicate 287. Find the Duplicate Number 442. Find All Duplicates in an Array 448. Find All Numbers Disappeared in an Array

    后面3个题都是限制在1-n的,所有可以不先排序,可以利用巧方法做.最后两个题几乎一模一样. 217. Contains Duplicate class Solution { public: bool ...

  7. 287. Find the Duplicate Number hard

    287. Find the Duplicate Number   hard http://www.cnblogs.com/grandyang/p/4843654.html 51. N-Queens h ...

  8. 【LeetCode】287. Find the Duplicate Number 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 保存已经访问过的数字 链表成环 二分查找 日期 题目 ...

  9. 【LeetCode】287. Find the Duplicate Number

    Difficulty:medium  More:[目录]LeetCode Java实现 Description Given an array nums containing n + 1 integer ...

随机推荐

  1. Python爬虫2----------运用代理访问

    为request添加一个代理,及将浏览器头部信息加入,随机从ip列表中拿出一个ip进行访问 注意函数参数的形式,如request.proxyhandler(协议,地址) import urllib.r ...

  2. Spring Security研究(2)-高级web特性

    1, 添加 HTTP/HTTPS 信道安全 <http> <intercept-url pattern="/secure/**" access="ROL ...

  3. CryptoTools加密与解密

    CryptoTools加密与解密 java常用的加密算法有MD5(Message Digest algorithm 5,信息摘要算法) BASE64(严格地说,属于编码格式,而非加密算法) SHA(S ...

  4. MapReduce极简教程

    一个有趣的例子 你想数出一摞牌中有多少张黑桃.直观方式是一张一张检查并且数出有多少张是黑桃?   MapReduce方法则是: 给在座的所有玩家中分配这摞牌 让每个玩家数自己手中的牌有几张是黑桃,然后 ...

  5. (转)添加PROPAGATION_REQUIRES_NEW 事务没有产生作用

    最近在做事务添加时  发现自己的事务没有新建,上网查到   仅用作收藏. 其二  注意  事务的注解  应该在 内层的事务上面 一.描述 Spring遇到嵌套事务时,当被嵌套的事务被定义为" ...

  6. ArrayList 和 LinkedList 的实现与区别

    (转载请标明出处) 1.ArrayLis t的实现 2.LinkedLis t的实现 3.ArrayList 和 LinkedList 的区别 ArrayList 的实现: 1.MyArrayList ...

  7. 【转】独立游戏如何对接STEAM SDK

    独立开发者在对接STEAM SDK之前 首先得先登上青睐之光,也就是我们俗称的"绿光" 一般要先对接G胖家的SDK,然后提交版本,最后等待审核... 我本身是unity 开发,对C ...

  8. 实际开发--->php时间函数

    当前日期(例:2017-10-04):date('Y-m-d',time()); 当前时间戳:strtotime(date('Y-m-d H-i-s',time()); 当前年月(例:2017-10) ...

  9. js-异步机制与同步机制

    Javascript的优势之一是其如何处理异步代码.异步代码会被放入一个事件队列,等到所有其他代码执行后才进行,而不会阻塞线程 1 理解异步代码: 1.1 JavaScript最基础的异步函数是set ...

  10. C-多个行内块布局

    1 消除间隔