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)