LeetCode287: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.

LeetCode:链接

LeetCode变体:LeetCode442:Find All Duplicates in an Array

这题要求不能修改数组,如果可以修改数组,就和剑指Offer_编程题50:数组中重复的数字是一样的!

第一种方法:二分法

实际上,我们可以根据抽屉原理简化刚才的暴力法。我们不一定要依次选择数,然后看是否有这个数的重复数,我们可以用二分法先选取n/2,按照抽屉原理,整个数组中如果小于等于n/2的数的数量大于n/2,说明1到n/2这个区间是肯定有重复数字的。比如6个抽屉,如果有7个袜子要放到抽屉里,那肯定有一个抽屉至少两个袜子。这里抽屉就是1到n/2的每一个数,而袜子就是整个数组中小于等于n/2的那些数。这样我们就能知道下次选择的数的范围,如果1到n/2区间内肯定有重复数字,则下次在1到n/2范围内找,否则在n/2到n范围内找。下次找的时候,还是找一半。

class Solution(object):
    def findDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        low = 1
        high = len(nums) - 1
        # 整个数组中如果小于等于n/2的数的数量大于n/2,说明1到n/2这个区间是肯定有重复数字的
        while low <= high:
            # 我们比较的mid而不是nums[mid]
            mid = (low + high) // 2
            count = 0
            for i in nums:
                if i <= mid:
                    count += 1
            # 必须和mid比较
            if count > mid:
                high = mid - 1
            else:
                low = mid + 1
        return low

第二种方法:双指针。

如果数组中元素不重复,那么,任意下标i和数组中该下标的值一一对应,如 对于数组 3,4,1,2,有如下对应关系:(注意,值从1~n)

  • 0 – > 2
  • 1  -> 4
  • 2  -> 1
  • 3  -> 3

设这个对应关系的运算函数为f(n) ,那么,我们从下标0出发,每次根据f取出下标i对应的值x,并将这个x作为下一次的下标,直到下标越界。

如3,4,1,2这个数组,那么有 0 – > 2-> 1-> 4

但如果有重复的话,中间就会产生多个映射,如3,4,1,2,3

  • 0 – > 2
  • 1  -> 4
  • 2  -> 1
  • 3  -> 3
  • 4  ->3

继续这么做的话,会发生 0 – > 2-> 1-> 4  -> 3 -> 3->3……

也就是最后一定是那个重复的元素。

这样类似于剑指Offer_编程题55:链表中环的入口结点找链表环路的起点,我们先用快慢两个下标都从0开始,快下标每轮运算两次,慢下标每轮运算一次,直到两个下标再次相同。这时候保持快下标位置不变,将慢下标移动到0,接着两个每轮分别运算一次,当这两个下标相遇时,就是环的起点,也就是重复的数

拿3,1,3,4,2举例,因为3是重复数字,所以从3开始就是一个环。

class Solution(object):
    def findDuplicate(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        slow, fast = nums[0], nums[nums[0]]
        # 拿链表对比 此时slow已经走了环的距离
        while slow != fast:
            slow = nums[slow]
            fast = nums[nums[fast]]
        fast = 0
        # 找到环的入口结点
        while slow != fast:
            slow = nums[slow]
            fast = nums[fast]
        return slow

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值