&LeetCode0004& 尋找兩個有序數組的中位數

題目

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

思路

首先,假設兩個有序數組的長度分別爲m和n,由於兩個數組長度之和 m+n 的奇偶性不確定,因此需要分情況來討論:對於奇數的情況,直接找到最中間的數即可,偶數的話需要求最中間兩個數的平均值。爲了簡化代碼,不分情況討論,使用一個技巧,分別找第 (m+n+1) / 2 個,和 (m+n+2) / 2 個,然後求其平均值,這對奇偶數都是適用的。若 m+n 爲奇數的話,那麼 (m+n+1) / 2(m+n+2) / 2 的值相等,相當於兩個相同的數字相加再除以2,還是其本身。
其次,定義一個函數在兩個有序數組中找到第K個元素,如何實現找到第K個元素?首先,爲了避免拷貝產生新的數組從而增加時間複雜度,使用兩個變量ij分別來標記數組 nums1nums2 的起始位置。然後來處理一些小問題,比如當某一個數組的起始位置大於等於其數組長度時,說明其所有數字均已經被淘汰,相當於一個空數組,實際上就變成了在另一個數組中找數字,當然直接就可以找出來。還有就是,如果 K=1 的話,只要比較 nums1nums2 的起始位置ij上的數字就可以了。難點就在於一般的情況怎麼處理?因爲需要在兩個有序數組中找到第K個元素,爲了加快搜索的速度,可以使用二分法,本題就是要對K二分,意思是需要分別在 nums1nums2 中查找第 K/2 個元素,注意,這裏由於兩個數組的長度不定,所以有可能某個數組沒有第 K/2 個數字,所以需要先 檢查 一下,數組中到底存不存在第 K/2 個數字,如果存在就取出來,否則就賦值一個整型最大值(目的是要在 nums1 或者 nums2 中先淘汰 K/2 個較小的數字,判斷的依據就是看 midVal1midVal2 誰更小,但如果某個數組的個數不到 K/2 個,自然無法淘汰,所以將其對應的 midVal 值設爲整型最大值,以保證其不會被淘汰),若某個數組沒有第 K/2 個數字,則淘汰另一個數組的前 K/2 個數字即可。
舉例說明,比如 nums1 = {3},nums2 = {2, 4, 5, 6, 7},K=4,要找兩個數組混合中第4個數字,則分別在 nums1nums2 中找第2個數字,而 nums1 中只有一個數字,不存在第二個數字,則 nums2 中的前2個數字可以直接跳過,爲啥呢,因爲要求的是整個混合數組的第4個數字,不管 nums1 中的那個數字是大是小,第4個數字絕不會出現在 nums2 的前兩個數字中,所以可以直接跳過。
最後,有沒有可能兩個數組都不存在第 K/2 個數字呢,這道題裏是不可能的,因爲K不是任意給的,而是給的 m+n 的中間值,所以必定至少會有一個數組是存在第 K/2 個數字的。最後就是二分法的核心,比較這兩個數組的第 K/2 小的數字 midVal1midVal2 的大小,如果第一個數組的第 K/2 個數字小的話,那麼說明要找的數字肯定不在 nums1 中的前 K/2 個數字,可以將其淘汰,將 nums1 的起始位置向後移動 K/2 個,並且此時的K也自減去 K/2,調用遞歸,舉個例子來說,比如 nums1 = {1, 3},nums2 = {2, 4, 5},K=4,要找兩個數組混合中第4個數字,那麼分別在 nums1nums2 中找第2個數字,nums1 中的第2個數字是3,nums2 中的第2個數字是4,由於3小於4,所以混合數組中第4個數字肯定在 nums2 中,可以將 nums1 的起始位置向後移動 K/2 個。反之,淘汰 nums2 中的前 K/2 個數字,並將 nums2 的起始位置向後移動 K/2 個,並且此時的K也自減去 K/2,調用遞歸即可。

C++代碼

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) 
    {
        int m = nums1.size(), n = nums2.size(), left = (m + n + 1) / 2, right = (m + n + 2) / 2;
        return (findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right)) / 2.0;
    }
    int findKth(vector<int>& nums1, int i, vector<int>& nums2, int j, int k) 
    {
        if (i >= nums1.size()) 
            return nums2[j + k - 1];
        if (j >= nums2.size()) 
            return nums1[i + k - 1];
        if (k == 1) 
        	return min(nums1[i], nums2[j]);

        int midVal1 = (i + k / 2 - 1 < nums1.size()) ? nums1[i + k / 2 - 1] : INT_MAX;
        int midVal2 = (j + k / 2 - 1 < nums2.size()) ? nums2[j + k / 2 - 1] : INT_MAX;

        if (midVal1 < midVal2) 
        {
            return findKth(nums1, i + k / 2, nums2, j, k - k / 2);
        } 
        else 
        {
            return findKth(nums1, i, nums2, j + k / 2, k - k / 2);
        }
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章