LeetCode 4. 兩個排序陣列的中位數

畫船聽雨發表於2018-07-06

題目描述:

題目描述

解題思路

1. 歸併法

首先想到的思路就是歸併排序中的歸併操作,將兩個有序的陣列進行重組之後O(1)的查詢到中位數元素,但是歸併的時間複雜度為O(n+m),與要求的O(log(m+ n))相差很多,因此不可行。

2. 分治法

因為時間複雜度要求為O(log(m+n))因此採用的是分治演算法這樣能保證時間複雜度是log級別。
但是如何進行分治是這道題的難點。
首先需要轉化一下思路,求中位數的含義既是:1. 當n為奇數時求(n/2+1)大的數;2. 當n為偶數時求(n/2)大與(n/2+1)大的數。因此這個問題可以轉化為求第k大的數的問題。
在兩個有序陣列中如何求第K大的數?
假設第一個陣列中前p項,第二個陣列中前q項滿足p+q = k-1,那麼第k大的數一定是p+1或者q+1位置上的數。
講到這裡就可以通過分治來將問題的規模縮小了。
我們令p=k/2, q = k-p;
1) 如果第一個陣列中第p個數 < 第二個陣列中第q個數,說明第一個陣列中前p個數一定小於第k大數,但是無法確定q位置代表的數字是否比第k個元素大,因此需要繼續尋找。此時可以將第一個陣列的元素減少p個,k -= p,來縮小範圍。
2) 如果第一個陣列中第p個數 > 第二個陣列中第q個數,同理說明第二個陣列中前1個數一定小於第k大數,但是無法確定p位置代表的數字是否比第k個元素大,因此需要繼續尋找。此時可以將第二個陣列的元素減少q個,k -= q,來縮小範圍。
3) 如果第一個陣列中第p個數 = 第二個陣列中第q個數,說明第K個元素為p或者q位置上的數字,因為他們之前的數字已經到達了k-1。

注意:

  1. k == 1時,說明需要找的數在當前剩下的兩個陣列的第一個元素中去一個小的;
  2. 當有一個陣列的長度為0了,說明只能在另一個陣列中取數,因此返回第k大元素即可;
  3. 為了保證判斷陣列是否為空,始終讓第一個陣列作為長度較短的陣列;

程式碼實現:

class Solution
{
public:

    double FindK(vector<int>& nums1, vector<int>& nums2,int start1, int start2, int len1, int len2, int k)
    {
        if(len1 > len2)
            return FindK(nums2, nums1, start2, start1, len2, len1, k);
        if(len1 == 0)
            return nums2[start2+k-1]*1.0;
        if(k == 1)
            return min(nums1[start1], nums2[start2])*1.0;

        int p = min(k/2, len1);
        int q = k-p;

        if(nums1[start1+p-1] < nums2[start2+q-1])
            return FindK(nums1, nums2, start1+p, start2, len1-p, len2, k-p);

        if(nums1[start1+p-1] > nums2[start2+q-1])
            return FindK(nums1, nums2, start1, start2+q, len1, len2-q, k-q);

        return nums1[start1+p-1];
    }
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2)
    {

        int n = nums1.size();
        int m = nums2.size();
        double Median = 0.0;
        int k = ((m+n)>>1) + 1;
        if(m == 1 && n == 1)
        {
            return (nums1[0]+nums2[0])*1.0/2;
        }
        if((n+m)&1)
        {
            Median = FindK(nums1, nums2, 0, 0, n, m, k);
        }
        else
        {
            //cout<<FindK(nums1, nums2, 0, 0, n, m, k-1)<<endl;
            Median = (FindK(nums1, nums2, 0, 0, n, m, k-1)+ FindK(nums1, nums2, 0, 0, n, m, k))/2.0;
        }
        return Median;
    }
};

相關文章