【每日一題】LeetCode. 4. 尋找兩個正序數組的中位數

每日一題,防止癡呆 = =

一、題目大意

給定兩個大小爲 m 和 n 的正序(從小到大)數組 nums1 和 nums2。
請你找出這兩個正序數組的中位數,並且要求算法的時間複雜度爲 O(log(m + n))。
你可以假設 nums1 和 nums2 不會同時爲空。
在這裏插入圖片描述
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays

二、題目思路以及AC代碼

前兩天要搞畢設的查重降重,無奈拖了兩天,今天就搞兩道吧 = =。

這道題有兩個思路,感覺第一個思路常規一些,也比較好想,第二個思路的話,看官解是懂了,但我覺得在做題的時候可能很難想到,看看就好。

思路一:數組中第k小的數

既然題目要求在兩個數組中求中位數,設兩個數組長度分別爲size_1和size_2,那中位數無非就是在總長度是奇數的時候,是第(size_1 + size_2) / 2 + 1個數;在總長度是偶數的時候,是第(size_1 + size_2) / 2 + 1個數和第(size_1 + size_2) / 2個數的平均值。

那麼我們只要找到求解兩個數組中第k小的數的方法,就可以求解該問題了。而又由於題目要求O(log(m+n))的複雜度,那麼不難想到二分。

想要求解兩個數組中第k小的數,設兩個數組爲A和B,我們可以每次比較A[k/2 - 1]和B[k/2 - 1]的大小,如果前者大,那麼B[k/2 - 1]則最大是第k-1個數,不可能是第k個,所以B[k/2 - 1]以及其之前的數都可以排除,然後將排除了多少個數,需要同時讓k減去多少,然後再在新的兩個數組上求第k小的數,直到k爲1,那麼此時兩個數組的第一個數,哪一個小,就是哪個。

當然,如果中途某一個數組的元素全部被排除,則直接返回另一個數組第k小的數即可(這裏k不再是最開始的k,而是更新之後的)。還有就是,如果下標k/2 - 1超過了數組長度,那直接比較數組最後一個元素即可。

思路二:劃分

思路一感覺還是挺好理解的,由於題目提示時間複雜度,也比較好想到二分。而這個思路,我覺得就很難想到了。

思想就是,中位數其實就把原數組分爲兩個大小一致的數組(或者差一個),其中一個數組中的值全部大於另一個數組中的值。這樣的話,我們要找中位數,其實就是找一個劃分的位置就可以了,又由於總長度是知道的,所以在兩個數組中的劃分位置是有確定關係的。

設在兩個數組中的劃分位置爲i和j,爲了保證劃分之後兩部分的長度一致,則要滿足 i + j = m - i + n - j, i + j = m - i + n - j + 1,前者是m+n爲偶數的情況,後者是m+n爲奇數,其中m是第一個數組的長度,n是第二個數組的長度。所以就可以統一用 i + j = (m + n + 1) / 2,這個式子來約束,因爲除是整除,所以對於m+n是奇數還是偶數均適用,這就滿足了第一個條件。

第二個條件是一部分的值必須均小於另一部分,那麼只要一部分的最大值小於另一部分的最小值就可以了,又因爲原數組是正序,那麼其實只要保證 A[i-1] <= B[j] 並且 B[j-1] <= A[i]即可,上面的條件又可以轉化爲在[0, m]中找一個最大的i,滿足A[i-1] <= B[j],我們把第二個條件轉化爲了找最大的i,因爲最大的i,意味着i+1是不滿足的,所以將i+1帶入,得到A[i] > B[j-1],即是第二個條件。

綜上所述,我們最後只要用二分法去查找[0, m],找到最大的i,滿足A[i-1] <= B[j]即可,其中i和j滿足上述提到的約束。至於爲什麼能用二分,是因爲隨着i的增大,A[i-1]增大,B[j]減小。

AC代碼

這裏就只給出了第一個思路的AC代碼,第二個思路我也就是理解了一下 = =

int getKthElement(vector<int>& nums1, vector<int>& nums2, int k) {
    int size_1 = nums1.size();
    int size_2 = nums2.size();

    int p_1 = k/2 - 1;
    int p_2 = k/2 - 1;
    int l_1 = 0;
    int l_2 = 0;

    while (l_1 < size_1 && l_2 < size_2) {
        if (k == 1) return nums1[l_1]<nums2[l_2]?nums1[l_1]:nums2[l_2];

        p_1 = min(l_1 + k/2-1, size_1 - 1);
        p_2 = min(l_2 + k/2-1, size_2 - 1);

        if (nums1[p_1] >= nums2[p_2]) {
            k -= (p_2 - l_2 + 1);
            l_2 = p_2 + 1;
        }
        else {
            k -= (p_1 - l_1 + 1);
            l_1 = p_1 + 1;
        }
    }

    if (l_1 < size_1) return nums1[l_1 + k - 1];
    return nums2[l_2 + k - 1];
}

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int total_len = nums1.size() + nums2.size();
        if (total_len & 1) {
            return getKthElement(nums1, nums2, total_len/2 + 1);
        }
        else {
            return (getKthElement(nums1, nums2, total_len/2 + 1) + getKthElement(nums1, nums2, total_len/2)) / 2.0;
        }
    }
};

如果有問題,歡迎大家指正!!!

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