LeetCode 04 Median of Two Sorted Arrays

題目要求

There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
??題目翻譯:給定兩個有序數組nums1和sums2,長度分別是m和n,求出兩個數組的中間值,要求算法的復雜度是O(log(m+n))。

題目分析

該問題可以抽象為一個數學問題:求第K小的值(或者第K大的值)。假若先合并兩個數組,復雜度是O(m+n),不符合要求。題目的復雜度要求O(log (m+n))給了我們提示:可以用二分查找來提高查找效率。算法設計采用遞歸二分查找,每一次遞歸截斷一半的查找空間。

函數的偽碼如下findk(int[] a, int m, int[] b, int n, int k)

  1. 假如數組a長度大于數組b,交換兩個數組,保證任何時候,數組a的長度小于等于數組b,簡化條件判斷
  2. 當數組a空,則返回數組b的第k個值,數組索引是k-1
  3. 當返回第一個最小值的時候(k=1),返回數組a和數組b最小值中較小的一個
  4. 截斷:pa = Math.min(k/2, m);pb = k - pa; 可得pa+pb = k,如果a[pa-1] < b[pb-1],顯然可證:a[pa-1]一定小于第k個值,又因為數組a有序,因此數組的第0到第pa-1個元素均小于第K個值,可以截斷,同時k = k-pa,縮小一半的查找空間。同理可證,a[pa-1] > b[pb-1]時,可截斷b[0……pb-1]部分,k = k-pb

代碼-Java實現

import java.util.*;

public class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int total = nums1.length + nums2.length;
        /**
         * 假設兩個數組長度之和是奇數,則中間數是第 total/2+1 個數
         * 假設兩個數組長度之和是奇數,則中間數是第 total/2 個數 和 第 total/2+1 個數的平均值
         */
        if (total%2 == 1) {
            return findk(nums1, nums1.length, nums2, nums2.length, total/2+1);
        } else {
            // 為什么傳進去的數組不會被修改?原因是findk中調用的是java.util.Arrays.copyOf()實現深拷貝
            // ToDO 偶數情況的尋找第 total/2+1 個數字可以在 total/2 上再做一次查找就可以,怎么實現這個優化?
            return (findk(nums1, nums1.length, nums2, nums2.length, total/2) +
                    findk(nums1, nums1.length, nums2, nums2.length, total/2+1)) / 2.0;
        }
    }
    
    // 遞歸查找第K個值
    public double findk(int[] a, int m, int[] b, int n, int k) {
        /**
         * 處理該問題中出現的遞歸的3個邊界條件
         */
        // 保證任何時候,數組a的長度小于等于數組b,簡化條件判斷
        if (m>n)
            return findk(b, n, a, m, k);
        // 當數組a空,則返回數組b的第K個值,數組索引是k-1
        if (m==0)
            return b[k-1];
        // 當返回第一個最小值的時候(k=1),返回數組a和數組b最小值中較小的一個
        if (k==1)
            return Math.min(a[0], b[0]);
        
        /**
         * pa+pb = k,如果a[pa-1] < b[pb-1],顯然可證:a[pa-1]一定小于第k個值,
         * 又因為數組a有序,因此數組的第0到第pa-1個元素均小于第K個值,可以截斷,
         * 同時k = k-pa, 縮小一半的查找空間。
         * 
         * 同理可證,a[pa-1] > b[pb-1] 時,可截斷b[0……pb-1]部分,k = k-pb。
         */
        int pa = 0, pb = 0;
        pa = Math.min(k/2, m); pb = k - pa;
        if (a[pa-1] < b[pb-1]){
            // java.util.Arrays.copyOf()實現深拷貝,內部實現是調用System.arraycopy
            a = Arrays.copyOfRange(a, pa, m);
            return findk(a, m-pa, b, n, k-pa);
        } else if (a[pa-1] > b[pb-1]) {
            // 同上一條注釋
            b = Arrays.copyOfRange(b, pb, n);
            return findk(a, m, b, n-pb, k-pb);
        } else {
            return a[pa-1];
        }
    }
    
    // 測試模塊
    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] sum1 = {3,4};
        int[] sum2 = {1,2};
        double result = solution.findMedianSortedArrays(sum1, sum2);
        System.out.println(result);
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容