【題解】LeetCode-尋找兩個有序數組的中位數(median-of-two-sorted-arrays)

4. 尋找兩個有序數組的中位數

給定兩個大小爲 m 和 n 的有序數組 nums1 和 nums2。

請你找出這兩個有序數組的中位數,並且要求算法的時間複雜度爲 O(log(m + n))。

你可以假設 nums1 和 nums2 不會同時爲空。

示例 1:

nums1 = [1, 3]
nums2 = [2]

則中位數是 2.0
示例 2:

nums1 = [1, 2]
nums2 = [3, 4]

則中位數是 (2 + 3)/2 = 2.5

思路一
對於這樣的題很自然的想法是:先合併,再求中位數。可是仔細想想似乎不滿足題幹對時間複雜度的要求,因爲合併所需要的時間是O(N),其中N = m + n,且在空間上也會有額外的消耗。顯然這不是最優的解法。(不過後來翻評論發現有哥們這樣做,而且基本都過了???)

解法一
可以參考歸併排序的merge部分,這裏就不做探討了。

思路二
很慚愧,翻了題解~
值得一說的是,筆者發現官方對這道題的解法數學味有點濃,於是花了點時間研究了一波,下面是一些收穫:

先明確一下概念:中位數把一組有序的數列劃分成兩個數量級相當的子集,左子集恆小於等於右子集
什麼意思呢?翻譯成僞代碼就是:

  1. COUNT(Left) == COUNT(Right);
  2. ANY(Left) <= ANY(Right);

咋一看好像沒什麼卵用,仔細一想其實不然,這個概念是可逆的,也就是說:如果某個數滿足這個概念,則其就是要找的中位數。

那麼不妨假設在第一個有序數集A中找到了一個下標爲x的數把A劃分爲等量的兩份,在數集B中找到的劃分點下標爲y,畫圖形象化就是:

                   |
     A[0]...A[x-1] | A[x]...A[m-1]
B[0],B[1]...B[y-1] | B[y],B[y+1]...B[n-1]
                   |

搞清楚了這個,就可以按圖索驥了:

第一步、根據第一個等量關係可以推導出:

  • m + n爲偶數時,(COUNT(A.Left) + COUNT(B.Left) = x + y) == (COUNT(A.Right)+COUNT(B.Right) = m - x + n - y)
  • m + n爲奇數時,(COUNT(Left) = x + y) == (COUNT(Right) = m - x + n - y + 1)
    也就是說:$ 2(x+y) = m + n + (m + n)%2 即: y = {{ m + n + {(m + n)} % 2 } \over 2} - x $
    函數形如:
    函數圖象

第二步、利用第一步分析結果:枚舉一個x就可以得到一個y,這個時候還需要滿足條件2:
由於AB初始是有序的: A[x-1] <= A[x],B[y-1] <= B[y];
此時若A[x-1] <= B[y] 且 B[y-1] <= A[x],則就相當於滿足了條件2。

第三步、找到xy後如何得到最終的中位數呢?
通過舉例的方式很快可以發現:

  • m + n爲奇數時,ans = Max(A[x-1], B[y-1])
  • m + n爲偶數時,ans = (Max(A[x-1], B[y-1]) + Min(A[x], B[y]))/2

解法二

class Solution {
    public double findMedianSortedArrays(int[] A, int[] B) {
        int m = A.length, n = B.length;
        if (m > n) { // 保證 m <= n, 確保j>=0
            int t = m;
            int[] T = A;
            m = n;
            A = B;
            n = t;
            B = T;
        }

        int a = 0, b = m, len = m + n;
        int halfLen = (len + (len) % 2) / 2;
        int x, y;

        while (a <= b) {
            x = a + (b - a) / 2;
            y = halfLen - x;
            if (x > a && A[x - 1] > B[y]) { // x取得太大了
                b = x - 1;
            } else if (x < b && A[x] < B[y - 1]) { // x取得太小了
                a = x + 1;
            } else { // x,y滿足了條件2
                int left;
                if (x <= 0) {
                    left = B[y - 1];
                } else if (y <= 0) {
                    left = A[x - 1];
                } else {
                    left = Math.max(A[x - 1], B[y - 1]);
                }

                if ((len) % 2 == 1) {
                    // m+n爲奇數
                    return left;
                }


                int right;
                if (x >= m) {
                    right = B[y];
                } else if (y >= n) {
                    right = A[x];
                } else {
                    right = Math.min(A[x], B[y]);
                }
                // m+n爲偶數
                return (left + right) / 2.0;

            }
        }
        return 0.0;
    }
}

其實可以發現,這個代碼好像跟官方的解答一毛一樣哎~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章