1. 程式人生 > >求兩個有序數組的中位數(4. Median of Two Sorted Arrays)

求兩個有序數組的中位數(4. Median of Two Sorted Arrays)

排序 font float 序列 大小 width 技術 display 個數

先吐槽一下,我好氣啊,想了很久硬是沒有做出來,題目要求的時間復雜度為O(log(m+n)),我猜到了要用二分法,但是沒有想到點子上去。然後上網搜了一下答案,感覺好有罪惡感。

題目原型

技術分享

正確的思路是:把問題轉化一下,假設任意給一個k值,求這兩個數組合並並按大小排序之後的第k個值。如此一來求中位數只是一個特例而已。

那如何搜索兩個有序序列中第k個元素呢,這裏又有個技巧。假設序列都是從小到大排列,對於第一個序列中前p個元素和第二個序列中前q個元素,我們想要的最終結果是:p+q等於k-1,且一序列第p個元素和二序列第q個元素都小於總序列第k個元素。因為總序列中,必然有k-1個元素小於等於第k個元素。這樣第p+1個元素或者第q+1個元素就是我們要找的第k個元素。

所以,我們可以通過二分法將問題規模縮小,假設p=k/2-1,則q=k-p-1,且p+q=k-1。如果第一個序列第p個元素小於第二個序列第q個元素,我們不確定二序列第q個元素是大了還是小了,但一序列的前p個元素肯定都小於目標,所以我們將第一個序列前p個元素全部拋棄,形成一個較短的新序列。然後,用新序列替代原先的第一個序列,再找其中的第k-p個元素(因為我們已經排除了p個元素,k需要更新為k-p),依次遞歸。同理,如果第一個序列第p個元素大於第二個序列第q個元素,我們則拋棄第二個序列的前q個元素。遞歸的終止條件有如下幾種:

  • 較短序列所有元素都被拋棄,則返回較長序列的第k個元素(在數組中下標是k-1)

  • 一序列第p個元素等於二序列第q個元素,此時總序列第p+q=k-1個元素的後一個元素,也就是總序列的第k個元素

註意

  • 每次遞歸不僅要更新數組起始位置(起始位置之前的元素被拋棄),也要更新k的大小(扣除被拋棄的元素)

偷來的源碼

  1 public class Solution {
  2     public double findMedianSortedArrays(int[] nums1, int[] nums2) {
  3         int m = nums1.length, n = nums2.length;
  4         int k = (m + n) / 2;
  5         if((m+n)%2==0){
  6             return (findKth(nums1,nums2,0,0,m,n,k)+findKth(nums1,nums2,0,0,m,n,k+1))/2;
  7
} else { 8 return findKth(nums1,nums2,0,0,m,n,k+1); 9 } 10 11 } 12 13 private double findKth(int[] arr1, int[] arr2, int start1, int start2, int len1, int len2, int k){ 14 // 保證arr1是較短的數組 15 if(len1>len2){ 16 return findKth(arr2,arr1,start2,start1,len2,len1,k); 17 } 18 if(len1==0){ 19 return arr2[start2 + k - 1]; 20 } 21 if(k==1){ 22 return Math.min(arr1[start1],arr2[start2]); 23 } 24 int p1 = Math.min(k/2,len1) ; 25 int p2 = k - p1; 26 if(arr1[start1 + p1-1]<arr2[start2 + p2-1]){ 27 return findKth(arr1,arr2,start1 + p1,start2,len1-p1,len2,k-p1); 28 } else if(arr1[start1 + p1-1]>arr2[start2 + p2-1]){ 29 return findKth(arr1,arr2,start1,start2 + p2,len1,len2-p2,k-p2); 30 } else { 31 return arr1[start1 + p1-1]; 32 } 33 } 34 }

求兩個有序數組的中位數(4. Median of Two Sorted Arrays)