LeetCode-4:尋找兩個正序數組的中位數

一、題目描述

在這裏插入圖片描述

二、解題思路

  題目要求時間複雜度爲O(log(m+n))O(log(m + n)),很明顯需要採用二分法來解決。
  如果我們可以在這兩個有序序列中找到第KK個數字,那麼我們只要能找到中間的一個或兩個數字,就一定可以得到這道題的解。
  所以問題退化爲尋找兩個有序序列的按序第KK個數字。
  在尋找中間的數字的過程中,需要區分以下兩個序列長度和爲奇數偶數的區別:

  • 如果爲奇數,那麼中間的數字只有一個,那就是第(len1+len2+1)/2(len1 + len 2 + 1) / 2個位置的元素
  • 如果是偶數,那麼中間的數字有兩個,分別第是(len1+len2+1)/2(len1 + len2 + 1) / 2(len1+len2)/2+1(len1 + len2) / 2 + 1個元素
  • 其實,奇數時,(len1+len2+1)/2(len1 + len 2 + 1) / 2(len1+len2+2)/2(len1 + len2 + 2) / 2的值是相等的;那麼就可以得出:
    • 當奇數時,中位數就是按序排列後的第(len1+len2+1)/2(len1 + len 2 + 1) / 2(len1+len2+2)/2(len1 + len 2 + 2) / 2個元素
    • 當偶數時,中位數就是按序排列後的第(len1+len2+1)/2(len1 + len 2 + 1) / 2(len1+len2+2)/2(len1 + len 2 + 2) / 2個元素的平均值

  確定了結果所需數字的位置,便可開始根據這個位置在兩個有序數組中找到這個位置的工作,這裏採用一個輔助函數來完成。

  • 如果起始位置大於等於數組長度,說明這個數組需要查找的部分已經變空,這個位置一定不可能在該數組裏,那麼直接返回另一個數組的第KK個位置
  • 如果K==1K == 1,那麼就直接返回兩個數組待查找部分兩個開頭元素的較小值
  • 否則,對KK進行二分,需要比較兩個數組裏偏移待查起始位置K/2K / 2處元素的大小,分別記爲half1half1half2half2
    • 如果half1<half2half1 < half2,說明第一個數組劃分範圍過大,導致其偏移K/2K / 2處的元素過小,需要縮小第一個數組的範圍到更大的部分,調整第一個數組的起始位置到後K/2K / 2
    • 反之,需要調整第二個數組的起始位置到後K/2K / 2
    • 調整的同時,還要縮小KK的範圍,這裏是把KK縮小到KK/2K - K / 2而不是K/2K / 2,原因在於如果KK爲偶數還好辦,如果爲奇數的話,比如說55,就會直接變成K=2K = 2,但實際上我們知道應該是變成K=3K = 3,這樣才滿足二分的定義,否則就過不去測試用例[1, 2]; [3, 4]

三、解題代碼

class Solution {
private:
    int findKth(vector<int>& num1, int start1, vector<int>& num2, int start2, int k){
        if(start1 >= num1.size())   return num2[start2 + k - 1];
        if(start2 >= num2.size())   return num1[start1 + k - 1];
        if(k == 1)  return min(num1[start1], num2[start2]);
        int half1 = (start1 + k / 2 - 1) < num1.size() ? num1[start1 + k / 2 - 1] : INT_MAX;
        int half2 = (start2 + k / 2 - 1) < num2.size() ? num2[start2 + k / 2 - 1] : INT_MAX;
        return half1 < half2 ? findKth(num1, start1 + k / 2, num2, start2, k - k / 2) : findKth(num1, start1, num2, start2 + k / 2, k - k / 2);
    }
public:
    double findMedianSortedArrays(vector<int>& num1, vector<int>& num2) {
        int len1 = num1.size();
        int len2 = num2.size();
        int left = (len1 + len2 + 1) / 2;
        int right = (len1 + len2 + 2) / 2;
        return (findKth(num1, 0, num2, 0, left) + findKth(num1, 0, num2, 0, right)) / 2.0;
    }
};

四、運行結果

在這裏插入圖片描述

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