1. 程式人生 > 其它 >確認訂單/提交訂單頁面

確認訂單/提交訂單頁面

給定兩個大小分別為 m 和 n 的正序(從小到大)陣列 nums1 和 nums2。請你找出並返回這兩個正序陣列的 中位數 。

演算法的時間複雜度應該為 O(log (m+n))

示例 1:

輸入:nums1 = [1,3], nums2 = [2]
輸出:2.00000
解釋:合併陣列 = [1,2,3] ,中位數 2
示例 2:

輸入:nums1 = [1,2], nums2 = [3,4]
輸出:2.50000
解釋:合併陣列 = [1,2,3,4] ,中位數 (2 + 3) / 2 = 2.5
 

 

提示:

nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-10^6 <= nums1[i], nums2[i] <= 10^6

方法一:合併排序

將兩個數組合並,並排序,時間複雜度取決於排序演算法,快排時間複雜度O((m+n)log(m+n))。缺點沒有使用兩個有序陣列這個條件。

方法二:有序陣列歸併

借用歸併排序的關鍵步驟(合併兩個有序陣列),時間複雜度O(m+n)。

當然也可以不合並陣列,這也是我最初的做法(C++):

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size();
        
int n = nums2.size(); bool is_oddnum = (m + n) % 2; //判斷是否為奇數 int now = 0, pre = 0; //如果是偶數,需要記錄兩個中位數 for (int i = 0, j = 0, k = 0; k <= (m + n) / 2; ++k) { //如果是奇數,迴圈結束now正好為中位數;如果是偶數,now為後一箇中位數 pre = now; //每次迴圈將pre變為now,而now在下面的步驟中更新 if (i >= m) { //如果第一個陣列遍歷完,那麼無需判斷直接將now更新為第二個陣列的當前元素。
now = nums2[j]; ++j; continue; } if (j >= n) { //同理 now = nums1[i]; ++i; continue; } if (nums1[i] < nums2[j]) { //判斷第一個陣列和第二個陣列的當前元素的大小,將now更新為小的元素 now = nums1[i]; ++i; } else { now = nums2[j]; ++j; } } if (is_oddnum) { return now; } else { return (pre + now) / 2.0; } } };

但是方法二的時間複雜度依然達不到要求。

方法三:二分查詢

根據中位數的定義,當 m+n 是奇數時,中位數是兩個有序陣列中的第 (m+n)/2個元素,當 m+n 是偶數時,中位數是兩個有序陣列中的第 (m+n)/2 個元素和第 (m+n)/2+1 個元素的平均值。因此,這道題可以轉化成尋找兩個有序陣列中的第 k 小的數,其中 k 為 (m+n)/2 或 (m+n)/2+1。

假設兩個有序陣列分別是 A 和 B。要找到第 k 個元素,我們可以比較A[k/2−1] 和 B[k/2−1],其中 / 表示整數除法。由於 A[k/2−1] 和 B[k/2−1] 的前面分別有 A[0..k/2−2] 和 B[0..k/2−2],即 k/2−1 個元素,對於 A[k/2−1] 和 B[k/2−1] 中的較小值,最多隻會有 (k/2−1)+(k/2−1)≤k−2 個元素比它小,那麼它就不能是第 k 小的數了。

因此我們可以歸納出三種情況:

如果 A[k/2−1]<B[k/2−1],則比 A[k/2−1] 小的數最多隻有 A 的前 k/2−1 個數和 B 的前 k/2−1 個數,即比 A[k/2−1] 小的數最多隻有 k−2 個,因此 A[k/2−1] 不可能是第 k 個數,A[0] 到 A[k/2−1] 也都不可能是第 k 個數,可以全部排除。

如果 A[k/2−1]>B[k/2−1],則可以排除 B[0] 到 B[k/2−1]。

如果 A[k/2−1]=B[k/2−1],則可以歸入第一種情況處理。

可以看到,比較 A[k/2−1] 和 B[k/2−1] 之後,可以排除 k/2 個不可能是第 k 小的數,查詢範圍縮小了一半。同時,我們將在排除後的新陣列上繼續進行二分查詢,並且根據我們排除數的個數,減少 k 的值,這是因為我們排除的數都不大於第 k 小的數。

有以下三種情況需要特殊處理:

如果 A[k/2−1] 或者 B[k/2−1] 越界,那麼我們可以選取對應陣列中的最後一個元素。在這種情況下,我們必須根據排除數的個數減少 k 的值,而不能直接將 k 減去 k/2。

如果一個數組為空,說明該陣列中的所有元素都被排除,我們可以直接返回另一個數組中第 k 小的元素。

如果 k=1,我們只要返回兩個陣列首元素的最小值即可。

class Solution {
public:
    int getKthElement(const vector<int>& nums1, const vector<int>& nums2, int k) {
        /* 主要思路:要找到第 k (k>1) 小的元素,那麼就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 進行比較
         * 這裡的 "/" 表示整除
         * nums1 中小於等於 pivot1 的元素有 nums1[0 .. k/2-2] 共計 k/2-1 個
         * nums2 中小於等於 pivot2 的元素有 nums2[0 .. k/2-2] 共計 k/2-1 個
         * 取 pivot = min(pivot1, pivot2),兩個陣列中小於等於 pivot 的元素共計不會超過 (k/2-1) + (k/2-1) <= k-2 個
         * 這樣 pivot 本身最大也只能是第 k-1 小的元素
         * 如果 pivot = pivot1,那麼 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把這些元素全部 "刪除",剩下的作為新的 nums1 陣列
         * 如果 pivot = pivot2,那麼 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把這些元素全部 "刪除",剩下的作為新的 nums2 陣列
         * 由於我們 "刪除" 了一些元素(這些元素都比第 k 小的元素要小),因此需要修改 k 的值,減去刪除的數的個數
         */

        int m = nums1.size();
        int n = nums2.size();
        int index1 = 0, index2 = 0;

        while (true) {
            // 邊界情況
            if (index1 == m) {
                return nums2[index2 + k - 1];
            }
            if (index2 == n) {
                return nums1[index1 + k - 1];
            }
            if (k == 1) {
                return min(nums1[index1], nums2[index2]);
            }

            // 正常情況
            int newIndex1 = min(index1 + k / 2 - 1, m - 1);
            int newIndex2 = min(index2 + k / 2 - 1, n - 1);
            int pivot1 = nums1[newIndex1];
            int pivot2 = nums2[newIndex2];
            if (pivot1 <= pivot2) {
                k -= newIndex1 - index1 + 1;
                index1 = newIndex1 + 1;
            }
            else {
                k -= newIndex2 - index2 + 1;
                index2 = newIndex2 + 1;
            }
        }
    }

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int totalLength = nums1.size() + nums2.size();
        if (totalLength % 2 == 1) {
            return getKthElement(nums1, nums2, (totalLength + 1) / 2);
        }
        else {
            return (getKthElement(nums1, nums2, totalLength / 2) + getKthElement(nums1, nums2, totalLength / 2 + 1)) / 2.0;
        }
    }
};

作者:LeetCode-Solution
連結:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-s-114/
來源:力扣(LeetCode)

 

題目連結:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/