题目描述
给定一个长度为 n+1 的数组nums,数组中所有的数均在 1∼n 的范围内,其中 n≥1。
请找出数组中任意一个重复的数,但不能修改输入的数组。
且只能使用 O(1)的额外空间
样例:
给定 nums = [2, 3, 5, 4, 3, 2, 6, 7]。
返回 2 或 3。
思路:
首先我们很容易就会想到可以采用哈希表的方法,依次扫描各元素,放入哈希表中,当元素已在哈希表中存在时,则找到了重复的数。但是此方法时间和空间复杂度均为O(n),与题目要求不符。
然后我们可能想到抽屉原理即:n+1 个苹果放在 n 个抽屉里,那么至少有一个抽屉中会放两个苹果。
用在这个题目中就是,一共有 n+1 个数,每个数的取值范围是1到n,所以至少会有一个数出现两次。
然后我们采用分治的思想,将每个数的取值的区间[1, n]划分成[1, n/2]和[n/2+1, n]两个子区间,然后分别统计两个区间中数的个数。
注意这里的区间是指 数的取值范围,而不是 数组下标。
具体实现可以设置一个上限和下限(h,L)
一个关键点k=int((h-L)/2)
将每个数按数值大小划分为两个区间,那重复的数必定在其中一个区间。
假如n是偶数,按最坏的原则分析,很显然那个数值区间的数多,重复的数就在那个数值区间。(在最坏的原则下不可能出现相等的情况)
假如n是奇数,同样按最坏的原则分析,n/2是向下取整,所以小于关键点的这个数值区间的数的个数(设为x)大于或等于另一数值区间的数的个数(设为y)时,重复的数都将在小于关键点的这个数值区间。(在最坏的原则下不可能出现x+1=y的情况)
综上两种情况当x>=y时,重复的数在小于关键点的这个数值区间,反之则在另一区间。
按如上方法确定区间后即可修改新的区间上下限,在新区间再按如上方法寻找,直到上下限相等,即找到重复的数字。
参考代码:
class Solution(object):
def duplicateInArray(self, nums):
"""
:type nums: List[int]
:rtype int
"""
h=int(len(nums))-1
L=0
z=h-L+1
while(h!=L):
k=int((h-L)/2)+L
y=0
for i in range(len(nums)):
if (nums[i]>k and nums[i]<=h):
y+=1
if (z-y)>=y:
h=k
z=z-y
else:
L=k+1
z=y
print(h)
或者
def duplicateInArray(nums):
nums=np.array(nums)
h=int(len(nums))-1
L=0
z=h-L+1
while(h!=L):
k=int((h-L)/2)+L
y=np.sum((nums>k) & (nums<=h))
if((z-y)>=y):
h=k
z=z-y
else:
L=k+1
z=y
print(L)