LeetCode題目詳解(三)——尋找兩個有序數組的中位數

參考:【分步詳解】兩個有序數組中的中位數和Top K問題

題目描述

給定兩個大小爲 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

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays

思路分析

一個增序數組

要尋求一個數組的中位數,很容易讓人聯想到用二分法解決

對於偶數個元素的數組A,假定其元素個數爲 n,下標從0開始,其中位數是 (A[(n-1)/2] + A[n/2])/2

對於偶數個元素的數組B,假定其元素個數爲m,下標從0開始,其中位數是A[m/2]

隱含條件:任何一個存在中位數的數組,其中位數都會將數組分割成等長的兩份,並且左邊小於中位數,右邊大於中位數

兩個增序數組

接下來推廣,從上面的結論很容易得出,對於兩個增序數組A,B,如果數組A中存在一個下標 i ,數組B中存在一個下標 j ,使得 len(數組A+數組B的左邊) == len(數組A+數組B的右邊),並且存在一個數使得A和B的左邊都比它小,A和B的右邊都比它大,那這個數就是這兩個增序數組的中位數。

left_part Cut right_part
A[0],A[1],…,A[i] / A[i+1],A[i+2],…,A[m-1]
B[0],B[1],…,B[j] / B[j+1],B[j+2],…,B[n-1]

用表達式來表示上面的兩個條件就是

  • i+j == m-1-i + n-1-j

  • A[i] <= B[j+1] && B[j] <= A[i+1]

雙數組長度的奇偶

一個數組的奇偶有兩種情況,可以想象,如果考慮雙數組的奇偶,將是會有 2*2 = 4種情況,這將會大大增加編程難度,那麼有沒有一種方法讓我們不用考慮奇偶。

讓數組恆爲奇數

操作很簡單,就是給每個元素的左右加一個字符,比如 ‘#’,長度變爲2*n+1,ex:

before len after len
{1,2,3} 3 {#,1,#,2,#,3,#} 7
{4,5,6,7} 4 {#,4,#,5,#,6,#,7,#} 9

這樣加的好處不光解決了奇偶的問題,還讓獲取元素的方法化簡了,對於任何一個元素,都可以通過 現在的位置/2 得到原來的位置,比如{#,1,#,2,#,3,#}中的2,它的位置是a[3],通過 3/2 == 1,可以得到原來的位置,{1,2,3}中的a[1] == 2,{#,4,#,5,#}中的5,位置是a[3],3/2 == 1,即{4,5,6,7}中的a[1] == 5

當然這個操作並不需要在代碼裏實現,這裏只是幫助理解

等式變換

接下來看這個等式

  • i+j == m-1-i + n-1-j

由於數組長度增加,第一個等式變爲

  • i+j == 2*m+1-1-i + 2*n+1-1-j
  • i+j == 2*m-i + 2*n-j
  • i+j == m+n

由此可得出 j == m+n-i,其中 i (0~2*m)

由於 j > 0,因此可以得出 m+n-i > 0,由 i 的取值範圍,最終得出此關係成立的條件是 n>m,即第二個數組的大小一定要比第一個大。

分治策略

接下來看這個等式

  • A[i] <= B[j+1] && B[j] <= A[i+1]

那麼分治的策略就很明顯了

如果 A[i] > B[j+1],說明A數組的左邊太長了,應該向左二分

如果 B[j] > A[i+1],說明A數組的左邊太短了,應該向右二分

臨界點

如果 i == 0 或 j == 0 或 i == 2*m 或 j == 2*n,那麼A[i+1],B[j+1]等等都不存在,此時,到達臨界的數組整體可能都比另一數組中的某一值小(大),那麼中值在另一數組中。

代碼

#include <bits/stdc++.h>

using namespace std;

class Solution
{
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
    {
        int m = nums1.size();
        int n = nums2.size();

        if(m>n)             //保證數組1長度<數組2長度
            return findMedianSortedArrays(nums2,nums1);

        int l1 = 0,l2 = 0,r1 = 0,r2 = 0,i = 0,j = 0,imin = 0,imax = 2*m; //下標爲0~2*m
        while(imin <= imax)   //存在空數組的情況,因此要有=
        {
            i = (imax+imin)/2;
            j = m+n-i;

            l1 = (i == 0)?INT_MIN:nums1[(i-1)/2];
            r1 = (i == 2*m)?INT_MAX:nums1[i/2];
            l2 = (j == 0)?INT_MIN:nums2[(j-1)/2];
            r2 = (j == 2*n)?INT_MAX:nums2[j/2];

            if(l1>r2)
                imax = i-1;
            else if(l2>r1)
                imin = i+1;
            else
                break;
        }
        return (max(l1,l2)+min(r1,r2))/2.0;
    }

};

int main()
{
    vector<int> a = {1,2,4,5};
    vector<int> b = {3};
    Solution Sve;
    cout<<Sve.findMedianSortedArrays(a,b);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章