LeetCode 287. 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.

Example 1:

Input: [1,3,4,2,2]
Output: 2

Example 2:

Input: [3,1,3,4,2]
Output: 3

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.

Reference Answer

思路分析

题目要求我们不能改变原数组,即不能给原数组排序,又不能用多余空间,那么哈希表神马的也就不用考虑了,又说时间小于O(n2),也就不能用brute force的方法,那我们也就只能考虑用二分搜索法了,我们在区间[1, n]中搜索,首先求出中点mid,然后遍历整个数组,统计所有小于等于mid的数的个数,如果个数小于等于mid,则说明重复值在[mid+1, n]之间,反之,重复值应在[1, mid-1]之间,然后依次类推,直到搜索完成,此时的low就是我们要求的重复值,参见代码如下:

这道题先 nums.sort(),再判定元素中是否存在相邻两个相同元素的解题方式是可以通过的,但是无意于题意相违背。而两层循环遍历使得数组元素做差判定是否存在0值的方式经验证,时间复杂度不能通过。

Code

class Solution:
    def findDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        left = 0
        right = len(nums)   
        while left < right:
            mid = left + (right - left)//2
            count = 0
            for x in nums:
                if x <= mid:
                    count += 1
            if count <= mid:
                left = mid+1
            else:
                right = mid
        
        return right
        
        

进阶版:

其实存在另外一种更快的解题思路,其核心思想快慢指针查看是否存在环,道理是因为有重复数字,并且题目限定了区间[1,n],所以可以巧妙的利用坐标和数值之间相互转换,而由于重复数字的存在,那么一定会形成环,我们用快慢指针可以找到环并确定环的起始位置,确实是太巧妙了!

环知识补充
在这里插入图片描述

设定快慢指针,初试位置为起点,快指针移动速度是慢指针的两倍,第一次相遇时slow走过的距离:a+b,fast走过的距离:a+b+c+b。
因为fast的速度是slow的两倍,所以fast走的距离是slow的两倍,有 2(a+b) = a+b+c+b,可以得到a=c(这个结论很重要!)。
我们发现L=b+c=a+b,也就是说,从一开始到二者第一次相遇,循环的次数就等于环的长度。

具体详情可参考 http://www.cnblogs.com/hiddenfox/p/3408931.html

对本题,借用此思想,实现代码如下:

class Solution:
    def findDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """     
		 # 借用判断环起点思想
        slow = nums[0]
        fast = nums[nums[0]]
        while slow != fast:
            fast = nums[nums[fast]]
            slow = nums[slow]
        fast = 0
        while slow != fast:
            fast = nums[fast]
            slow = nums[slow]
        return fast

        

Note:

  • 这道题很巧妙,题目难度不大,但通过限制一系列条件,很能考研功底,其中二分法应该是作为基础方法掌握;而借用查找环起点思想则无疑是进一步提升。

参考资料

[1] http://www.cnblogs.com/hiddenfox/p/3408931.html
[2] https://blog.csdn.net/fuxuemingzhu/article/details/79530847
[3] http://www.cnblogs.com/grandyang/p/4843654.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值