1. 程式人生 > >LeetCode:4. Median of Two Sorted Arrays

LeetCode:4. 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)).

You may assume nums1 and nums2 cannot be both empty.

Example 1:

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

The median is 2.0 Example 2:

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

The median is (2 + 3)/2 = 2.5

題目是找出兩個有序陣列的中位數。
解法來源:
https://leetcode.com/problems/median-of-two-sorted-arrays/discuss/2481/Share-my-O(log(min(mn))-solution-with-explanation?page=15
此題的解法請去看原解答,十分清晰。我這裡記錄下來的只是我自己看解答過程中的一個小困惑的一些想法,這裡沒有涉及此題的解法。

其實這題很容易就想到的是先歸併排序,然後找中位數。但是如果用歸併排序的話時間複雜度是O(m+n),不符合題目要求。

這題有一個很煩人,很讓人困惑但是繞不過去的點,即關於用一個有序陣列的哪個索引來表示中位數:
我們規定,該索引將陣列分為左右兩部分,而且該索引指向的那個位置屬於右邊的那部分。如果陣列長度為偶數,那麼左右兩部分長度相等;如果陣列長度為技術,那麼左邊長度比右邊長度多一。所以如何計算該索引呢?答案是(len+1)/2。
比如長度為4,(4+1)/2=2,0到1是左邊,2到3是右邊,左右長度相等,2屬於右邊。
比如長度為5,(5+1)/2=3,0到2是左邊,3到4是右邊,左邊比右邊長1,3屬於右邊。
以後遇到類似的題目都遵循這個規定會簡化思考的負擔。

整個解法的大致思路是:
假設陣列A和B的長度分別為m和n。取0<=i<=m, 0<=j<=n,A[0]到A[i-1]和B[0]到B[j-1]屬於較小的一半,A[i]到A[m-1]和B[j]到B[n-1]屬於較大的另一半,要使得兩邊的數目相等或者相差1,要滿足以下兩個條件:
1.i + j = m - i + n - j + 1
2.A[i-1] <= B[j] && B[j-1] <= A[i]
這裡如果n >= m,可將條件一化為j = (m+n+1)/2-i,否則j可能為負。所以如果m < n的話,需要在程式碼開頭將他們swap。至於這裡的+1,就是因為上面所說的計算中位數的索引。
還有需要考慮邊界條件,也就是比如i = 0的時候,不能取A[i-1],這裡不談。
整個程式碼的過程就是需要取i,然後根據i計算出j,然後看看符不符合第二個條件。如果符合,則說明找到合適的;如果沒有,根據大小關係,調整i的大小,再往復迴圈。

做這個題目的一個收穫就是學習了這個演算法,還有一個就是這樣一個思想:如果有兩個變數,而且有一個不等式,一個等式,可以用等式來將一個變量表示為另一個變數,然後用不等式來驗證是否正確。

下面是程式碼:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        using std::swap;
        if (nums1.size() > nums2.size()) 
            swap(nums1, nums2);
        size_t m = nums1.size(), n = nums2.size();
        if (!n)
            return 0;
        size_t i_min = 0, i_max = m;
        size_t i, j;
        while (i_min <= i_max) {
            i = (i_min + i_max) / 2;
            j = (m + n + 1) / 2 - i;
            if (j > 0 && i < m && nums2[j-1] > nums1[i])
                i_min = i + 1;
            else if (i > 0 && j < n && nums1[i-1] > nums2[j])
                i_max = i - 1;
            else {
                int max_of_left;
                if (i == 0) max_of_left = nums2[j-1];
                else if (j == 0) max_of_left = nums1[i-1];
                else max_of_left = max(nums1[i-1], nums2[j-1]);
                
                if ((m+n) % 2) 
                    return max_of_left;
                
                int min_of_right;
                if (i == m) min_of_right = nums2[j];
                else if (j == n) min_of_right = nums1[i];
                else min_of_right = min(nums1[i], nums2[j]);
                
                return (double)(max_of_left + min_of_right) / 2.0;
            }
        }
        return 0.0;
    }
};