Leetcode 4 Median of Two Sorted Arrays

这个题目,如果没有找到正确的切入点会非常绕(太多边界条件要处理),一旦姿势用对,就非常简单了。

 

首先不解释,是个二分。但是第一步是:扔掉二分,将其一般化。generalize it!

具体来说怎么“一般化”,

  1.不是找中位数,而是找第k大的数

  2.不从两个数组的中间位置找,而是从任意位置找。

第二步是构画答案的样子。这个在很多题目当中都有用,先假设出答案,再分析答案应该有什么样的性质。

 

对于n1中任意位置a的一个数值x,即n1[a]=x,则在数组n1上,x左侧有a个数,右侧有len(n1)-a-1个数,为了方便将b=len(n1)-a-1。

用圈圈o表示一个数,则:o..ooo, x ,ooo...ooo,左侧a个o,右侧b个o

对于n2同理,假如任意取下标n2[c],拿到其值为y,则o..ooo, y ,ooo...ooo,左侧c个o,右侧d个o,其中d=len(n2)-c-1

 

不失一般性,假设x<y,则我们知道,y同时大于x左边的a个数,所以在最终形成的n1+n2数组中:

  1.y>(n1中x以及x左边的所有a个数字,共a+1个)

  2.y>(n2中y本身左边的所有c个数字,共c个)

所以最终数组中,y一共至少大于a+c+1个数,之所以说至少,因为x右边的数字与y的大小我们不知道,也不关心。

同理,x一共至少小于b+d+1个数。

 

假设最终结合的数组:ooo..oooo●●●...●●●,其中前面o有a+c+1个,后面●有b+d+1个

那么x的位置肯定是o中的某一个,因为如果x是●中的一个,那么将不满足“x一共至少小于b+d+1个数”,

同理y的位置肯定是●中的某一个。

 

当前结论下,我们如果要找n1+n2中的第k大的数,那我们首先看k属于前半段ooo..oooo当中,还是后半段●●●...●●●当中。

假如在前半段中,那么我们可以得出这个第k大的数字一定小于y,因为y在其右边。

所以我们将原来的n2=c+1+d中的中间的y和后面的d个数一并扔掉,只保留前面c个数,仍然保证答案存在,于是变成了“在n1和新的n2种找第k大的数”,递归开始。

假如在后半段也一样,只不过因为扔掉的是n1的前半段a+1个数,所以要更新k值,k=k-(a+1),也开始递归。

 

然后从一般到特殊,如何选择n1和n2的x和y值呢,直觉:取中间值做二分呗。因为我们每次扔掉的是前半段(或后半段)以及中间一个值(x或y),这就保证了我们每次递归,至少扔掉 n1/2向上取整 或者 n2/2向上取整个数字,所以可以保证log(n)+log(m)的复杂度(你可以想想为啥不是log(n+m))

 

跳出递归返回条件:len(n1)==0 或者len(n2)==0

 

代码:

class Solution:
    def find_Kth(self,n1,n2,k):
        if len(n1)==0:
            return n2[k]
        if len(n2)==0:
            return n1[k]
        m1,m2=len(n1)//2,len(n2)//2
        if n1[m1]>n2[m2]:
            n1,n2=n2,n1
            m1,m2=m2,m1
        l,r=m1+m2,m1+m2+1
        if k<=l:
            n2=n2[:m2]
        else:
            k-=m1+1
            n1=n1[m1+1:]
        return self.find_Kth(n1,n2,k)
    def findMedianSortedArrays(self, nums1, nums2):
        l=len(nums1)+len(nums2)
        if l%2==0:
            return (self.find_Kth(nums1,nums2,l//2)+self.find_Kth(nums1,nums2,l//2-1))/2
        else:
            return self.find_Kth(nums1,nums2,l//2)

  

 

posted @ 2022-04-18 18:01  Cloud.9  阅读(27)  评论(0编辑  收藏  举报