不修改数组找出重复的数字(分治的思想--python实现)

题目描述

给定一个长度为 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)

你可能感兴趣的:(算法,算法,python)