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