LeetCode 287. Find the Duplicate Number

简介: 给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。

v2-babd6d872b12008d3c80a5bdcfc52b77_1440w.jpg

Description



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:

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 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。


示例 1:

输入: [1,3,4,2,2]

输出: 2


示例 2:

输入: [3,1,3,4,2]

输出: 3


说明:

不能更改原数组(假设数组是只读的)。

只能使用额外的 O(1) 的空间。

时间复杂度小于 O(n2) 。

数组中只有一个重复的数字,但它可能不止重复出现一次。


思路



1. 二分法


  • 我们用left,right来表示重复的数字所在的范围.对于一组连续递增的数(如1,2,3,4……n),我们取中间值middle,middle把这组数分成了左右两边(我们把middle归为左边),则
  • 1.如果有奇数个,则左边数的个数 == 右边数的个数+1,这时我们添加一个数使得原数组出现重复,如果重复的数落在了左边,则左边的数>右边的数;如果重复的数出现在了右边,则左边数的个数 == 右边数的个数.
  • 2.如果有偶数个,则左边数的个数 == 右边的个数,这时我们添加一个数使得原数组出现重复,如果重复的数落在了左边,则左边的数>右边的数;如果重复的数出现在了右边,则左边数的个数 < 右边数的个数.
  • 所以如果重复的数出现在左边,则一定有左边数的个数>右边的数的个数;如果重复的数出现在右边,则左边数的个数<=右边数的个数.
  • 于是对于题中给定的数组,我么用count来统计小于等于middle的数,如果count<=middle(middle刚好表示数组左边的数的个数),说明重复的数在middle的右边,即重复的数比middle大,我们更新left=middle+1;如果count>middle说明重复的数比middle小,我们更新right = middle-1


2.环形链表



v2-5e9c1f4472963b63ffe8ab17f5143d9c_720w.jpg


  • 如上图,我们将第一个索引初始化为0,然后将数组0号元素的值作为下一次访问的索引,如此下去,会发现访问会形成一个环,并且重复元素为环的第一个位置.


# -*- coding: utf-8 -*-
# @Author:             何睿
# @Create Date:        2019-02-07 16:19:11
# @Last Modified by:   何睿
# @Last Modified time: 2019-02-08 19:29:25
class Solution:
    def findDuplicate(self, nums: 'List[int]') -> 'int':
        # 二分法,left,right初始化数组的范围
        # left,right并不指向元素的索引,而是表示重复的数字所在的范围
        left, right = 1, len(nums) - 1
        while left <= right:
            # count用来统计小于等于middle的数的个数
            count = 0
            middle = ((right - left) >> 1) + left
            # 统计小于等于middle的元素的个数
            for num in nums:
                if num <= middle: count += 1
            # 如果小于等于middle的数字个数大于middle,说明重复的数字比middle小
            if count > middle: right = middle - 1
            # 如果小于等于middle的数字个数小于middle,说明重复的数字比middle大
            if count <= middle: left = middle + 1
            # 我们不断缩小left,right的区间,直到最后找到重复的数字
        return left
    def findDuplicate2(self, nums: 'List[int]') -> 'int':
        # 方法二把这道题转换成了一个带环的链表,求环的起始位置
        slow, fast = 0, 0
        while True:
            # slow指针每次向后走一步
            slow = nums[slow]
            # fast指针每次向后走两步
            fast = nums[nums[fast]]
            if slow == fast: break
        fast = 0
        # fast和slow一定会在头部相遇
        while fast != slow:
            slow = nums[slow]
            fast = nums[fast]
        return slow


源代码文件在这里.


目录
相关文章
|
Java
Leetcode 295. Find Median from Data Stream
在一个有序数组中找中位数,但需要支持再数组中添加新的元素。本来是有序里的,可以很轻易就查到中位数,但如果添加新数字后,不一定有序。如果先对数组排序,那代价就比较大了,每次排序时间复杂度O(n*log(n)),看discuss发现了一种很巧妙的解法,可以把添加数据的时间复杂度降低到O(log(n)) ,查询中位数O(1)。
51 0
|
算法
Leetcode 313. Super Ugly Number
题目翻译成中文是『超级丑数』,啥叫丑数?丑数就是素因子只有2,3,5的数,7 14 21不是丑数,因为他们都有7这个素数。 这里的超级丑数只是对丑数的一个扩展,超级丑数的素因子不再仅限于2 3 5,而是由题目给定一个素数数组。与朴素丑数算法相比,只是将素因子变了而已,解法还是和朴素丑数一致的。
99 1
|
5月前
|
存储 SQL 算法
LeetCode 题目 65:有效数字(Valid Number)【python】
LeetCode 题目 65:有效数字(Valid Number)【python】
|
6月前
|
存储 算法
【LeetCode力扣】单调栈解决Next Greater Number(下一个更大值)问题
【LeetCode力扣】单调栈解决Next Greater Number(下一个更大值)问题
45 0
Leetcode Find Minimum in Rotated Sorted Array 题解
对一个有序数组翻转, 就是随机取前K个数,移动到数组的后面,然后让你找出最小的那个数,注意,K有可能是0,也就是没有翻转。
48 0
|
存储
Leetcode Single Number II (面试题推荐)
给你一个整数数组,每个元素出现了三次,但只有一个元素出现了一次,让你找出这个数,要求线性的时间复杂度,不使用额外空间。
39 0
|
算法 Python
LeetCode 1160. 拼写单词 Find Words That Can Be Formed by Characters
LeetCode 1160. 拼写单词 Find Words That Can Be Formed by Characters
LeetCode 1160. 拼写单词 Find Words That Can Be Formed by Characters
|
JavaScript 索引
LeetCode 436. Find Right Interval
Given a set of intervals, for each of the interval i, check if there exists an interval j whose start point is bigger than or equal to the end point of the interval i, which can be called that j is on the "right" of i.
82 0
LeetCode 436. Find Right Interval
|
算法
LeetCode 414. Third Maximum Number
给定一个非空数组,返回此数组中第三大的数。如果不存在,则返回数组中最大的数。要求算法时间复杂度必须是O(n)。
94 0
LeetCode 414. Third Maximum Number
|
算法
LeetCode 405. Convert a Number to Hexadecimal
给定一个整数,编写一个算法将这个数转换为十六进制数。对于负整数,我们通常使用 补码运算 方法。
95 0
LeetCode 405. Convert a Number to Hexadecimal