【LeetCode】0004——尋找兩個有序數組的中位數

題目描述

在這裏插入圖片描述

解題思路

中位數需要根據兩個數組長度和的奇偶決定:

  • 假設nums1.length = m,nums2.length = n
  • (m + n) % 2 == 0,表示兩數組長度之和爲偶數,中位數則是中間兩個數
  • 否則爲奇數,中位數是中間的數

但是我們可以不同通過分別考慮來計算最終值,只需要通過第(m + n + 1) / 2個數 + 第(m + n + 2) / 2個數除以2就可以得到最終結果

1、不考慮複雜度

  如果我們不考慮算法的複雜度,很容易想到,將兩個數組拼成一個大數組,然後在大數組中求中位數就OK了,此時算法的複雜度是O(m+n)

這裏只給出 Java 代碼:

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n1 = nums1.length;
        int n2 = nums2.length;
        int len = n1 + n2;
        
        int[] merge = new int[len];
        int i = 0, j = 0, k = 0;
        //n1 = n2
        while(i < n1 && j < n2){
            if(nums1[i] < nums2[j]){
                merge[k] = nums1[i];
                i++;
                k++;
            }else{
                merge[k] = nums2[j];
                j++;
                k++;
            }
        }
        //n1 > n2
        while(i < n1){
            merge[k] = nums1[i];
            i++;
            k++;
        }
        //n1 < n2
        while(j < n2){
            merge[k] = nums2[j];
            j++;
            k++;
        }
        
        double res = 0.0;
        //取中位數
        if(len % 2 == 0){
            res = (merge[len/2 - 1] + merge[len/2]) * 1.0 / 2;
        }else{
            res = merge[len/2];
        }
        return res;        
    }
}

2、雙指針解法

(1)使兩個指針分別從兩個數組的開頭位置開始,比較兩個指針所指向的數的大小,誰小誰就往後移動;

(2)通過計算得到中位數的位置,得到兩指針總共需要移動的次數 k

  • m + n爲偶數,應該移動 (m + n) / 2 - 1(m + n) / 2
  • m + n爲奇數,應該移動 (m + n) / 2

另外需要注意的是,邊界條件的考慮,如果其中一個數組已經到最後,但是還沒有達到k次,那麼就讓另一個指針繼續往後移動

Java代碼:

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n1 = nums1.length;
        int n2 = nums2.length;
        int len = n1 + n2;
        
        int idx1 = 0;
        int idx2 = 0;
        int x = 0, y = 0;
        
        while(idx1 + idx2 < len){
            if(idx1 < n1){
                while(idx2 == n2 || nums1[idx1] <= nums2[idx2]){
                    idx1++;
                    if(idx1 + idx2 == (len + 1) / 2){
                        x = nums1[idx1 - 1];
                    }
                    if(idx1 + idx2 == (len + 2) / 2){
                        y = nums1[idx1 - 1];
                        return (x + y) * 1.0 / 2;
                    }
                    if(idx1 == n1){
                        break;
                    }
                }
            }
            if(idx2 < n2){
                while(idx1 == n1 || nums2[idx2] <= nums1[idx1]){
                    idx2++;
                    if(idx1 + idx2 == (len + 1) / 2){
                        x = nums2[idx2 - 1];
                    }
                    if(idx1 + idx2 == (len + 2) / 2){
                        y = nums2[idx2 - 1];
                        return (x + y) * 1.0 / 2;
                    }
                    if(idx2 == n2){
                        break;
                    }
                }
            }
        }
        return -1;
    }
}

3、二分法

通過以上分析,我們可以發現,其實我們就是需要找到第(m + n + 1) / 2個數 + 第(m + n + 2) / 2個數即可。

但是我們需要對各種情況進行討論:

  • 當一個數組比較短的時候,中位數都在另一個數組中,該如何處理
  • 如何獲得 第(m + n + 1) / 2個數和第(m + n + 2) / 2個數

在這裏插入圖片描述

此外,需要注意的是,當一個數組太短,導致中位數在另一個數組中時,要考慮到邊界判斷
上圖只查找了一個,另一個類似,或者以第一個爲基礎繼續查找

Java代碼

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n1 = nums1.length;
        int n2 = nums2.length;
        int len = n1 + n2;
        
        int md1 = (len + 1) / 2;
        int md2 = (len + 2) / 2;
        
        return (getMdVal(nums1, 0, nums2, 0, md1) + getMdVal(nums1, 0, nums2, 0, md2)) * 1.0 / 2;
    }
    
    public static int getMdVal(int[] A, int Astart, int[] B, int Bstart, int k){
        if (Astart > A.length-1){
            return B[Bstart + k -1];
        }
        if (Bstart > B.length-1){
            return A[Astart + k -1];
        }
        if (k == 1){
            return Math.min(A[Astart],B[Bstart]);
        }
 
        int Amin = 0, Bmin = 0;
        if (Astart + k/2 -1 < A.length){
            Amin = A[Astart + k/2 -1];
        }
        if (Bstart + k/2 -1 < B.length){
            Bmin = B[Bstart + k/2 -1];
        }
 
        return Amin < Bmin ? getMdVal(A, Astart + k/2, B, Bstart, k-k/2):getMdVal(A, Astart, B, Bstart+k/2, k-k/2);
    }
}

Python3代碼

class Solution:    
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        m, n = len(nums1), len(nums2)
        length = m + n
        
        md1 = (length + 1) // 2
        md2 = (length + 2) // 2
        def getMdVal(A, Astart, B, Bstart, k):
            if Astart > (len(A) - 1):
                return B[int(Bstart + k - 1)]
            if Bstart > (len(B) - 1):
                return A[int(Astart + k - 1)]
            if k == 1:
                return A[int(Astart)] if (A[int(Astart)] < B[int(Bstart)]) else B[int(Bstart)]

            Amin, Bmin = 2**31-1, 2**31-1
            if (Astart + k//2 - 1) < len(A):
                Amin = A[int(Astart + k//2 - 1)]
            if (Bstart + k//2 - 1) < len(B):
                Bmin = B[int(Bstart + k//2 - 1)]
            return getMdVal(A, Astart + k//2, B, Bstart, k-k//2) if Amin < Bmin else getMdVal(A, Astart, B, Bstart+k//2, k-k//2)
        
        return (getMdVal(nums1, 0, nums2, 0, md1) + getMdVal(nums1, 0, nums2, 0, md2)) / 2

在這裏插入圖片描述

運行結果不怎麼好

class Solution:
    def findMedianSortedArrays(self, nums1, nums2):
        nums = nums1 + nums2
        nums.sort()
        length = len(nums)
        if length == 2:
            return (nums[0] + nums[1])/2
        if length % 2 == 0:
            return (nums[length // 2 - 1] + nums[(length // 2)])/2
        return nums[length // 2]  

在這裏插入圖片描述

此代碼是先拼接,在查找的,貌似兩個結果差不多

C++代碼

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size();
        int n = nums2.size();
        int md1 = (m + n + 1) / 2;
        int md2 = (m + n + 2) / 2;
        return (getMdVal(nums1, 0, nums2, 0, md1) + getMdVal(nums1, 0, nums2, 0, md2)) / 2.0;
    }
    int getMdVal(vector<int>& A, int Astart, vector<int>& B, int Bstart, int k) {
        if (Astart >= A.size()) return B[Bstart + k - 1];
        if (Bstart >= B.size()) return A[Astart + k - 1];
        if (k == 1) return min(A[Astart], B[Bstart]);
        int midVal1 = (Astart + k / 2 - 1 < A.size()) ? A[Astart + k / 2 - 1] : INT_MAX;
        int midVal2 = (Bstart + k / 2 - 1 < B.size()) ? B[Bstart + k / 2 - 1] : INT_MAX;
        if (midVal1 < midVal2) {
            return getMdVal(A, Astart + k / 2, B, Bstart, k - k / 2);
        } else {
            return getMdVal(A, Astart, B, Bstart + k / 2, k - k / 2);
        }
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章