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個數字)
2.y>(n2中y本身左邊的所有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)