【算法剖析】尋找兩個已序數組中的第k大元素


1、問題描述

  給定兩個數組AB,其大小分別為mn,假定它們都是已按照增序排序的數組,我們用盡可能快的方法去求兩個數組合並后第k大的元素,其中,1\le k\le(m+n)。例如,對於數組A=[1,3,5,7,9]B=[2,4,6,8]。我們記第k大的數為max_{k-th},則k=4時,max_{4-th}=4。這是因為排序之后的數組A+B=[1,2,3,4,5,6,7,8,9],第4大的數是4。我們針對這一個問題進行探討。

2、算法一

  第一眼看到這個題的時候,我們能夠很快地想出來最基本的一種解法:對數組AB進行合並,然后求出其第k大的數,即找到答案。合並的過程,我們可以參考歸並排序的合並子數組的過程,時間復雜度為O(m+n)。下面給出算法:

int findKthMaxNumOfArrays(int *a,int m,int *b,int n,int k)
{
    int *p=a;
    int *q=b;
    int i=0;
    int j=0;
    int cur=0;
    while(i<m&&j<n)
    {
        if(a[i]<b[j])
        {
            cur++;
            if(cur==k) return a[i];
            i++;
        }
        else 
        {
            cur++;
            if(cur==k) return b[j];
            j++;
        }
    }
    while(i<m)
    {
        cur++;
        if(cur==k) return a[i];
        i++;
    }
    while(j<n)
    {
        cur++;
        if(cur==k) return b[j];
        j++;
    }
}
View Code

3、算法二

  實際上算法一的時間復雜度已經是線性的了。可是,是否存在更快的算法能夠完成這項任務呢?答案是肯定的,時間復雜度可以縮短到O(log(m+n))時間內。在這種算法中,二分的思想十分重要。我們將數組A分為兩半,前一部分的大小為\left \lfloor \frac{m}{2} \right \rfloor,后一部分為m- \left \lfloor \frac{m}{2} \right \rfloor;數組B同時分為這樣兩部分,第一部分的大小為\left \lfloor \frac{n}{2} \right \rfloor,第二部分的大小為n- \left \lfloor \frac{n}{2} \right \rfloor。如下圖所示:

通過a_{\frac{m}{2}}b_{\frac{n}{2}},我們將每個數組分為2部分,分別記為A1A2B1B2。假定b_{\frac{n}{2}} \ge a_{\frac{m}{2}},如果不是,我們只需要交換AB兩個數組即可。接下來,我們看第k大的數落在了哪個區間里面,令t=a_{\frac{m}{2}}+b_{\frac{n}{2}}+1,這個t實際上是包含了A1a_{\frac{m}{2}}B1。如果k\le t時,則說明max_{k-th}肯定不在B2里面,這是由於:B2中的所有數\ge b_{\frac{n}{2}},而b_{\frac{n}{2}} \ge A1,B1中的所有數與a_{\frac{m}{2}},而這部分數總共有t個,說明b_{\frac{n}{2}}起碼是t+1個,若max_{k-th}出現在B2中,則說明k\ge t+1,與假設矛盾。我們可以得出該結論。因此,在判斷之后,我們可以剔除數組BB2部分,然后再在新數組中尋找;另外,如果k\ge t,則說明max_{k-th}肯定不在A1部分,這部分的證明同上一個證明相同,不再贅述。同樣地,在判斷之后,我們可以剔除數組AA1部分,然后再在新數組中尋找。基於這樣一種思想,我們每次迭代,都刪除了其中一個數組中一半的元素,時間復雜度大約可認為是O(log(m+n))

  在實現的時候,我們需要特別注意邊界條件,詳細的代碼如下:

int findKthMaxNumOfArrays(int *A, int m, int *B, int n, int k)
{
        if(m == 0)return B[k-1];
        if(n == 0)return A[k-1];
        int i = m>>1, j = n>>1, *p, *q, t;
        if(A[i] <= B[j])p = A, q = B;
        else p = B, q = A, swap(i, j), swap(m, n);
        t = i + j + 1;
        if(t >= k)return findKthMaxNumOfArrays(p, m, q, j, k);
        else if(t < k)return findKthMaxNumOfArrays(p+i+1, m-i-1, q, n, k-i-1);
}
算法二

4、擴展問題

  通過算法二,我們很容易地解決一個類似的問題:求兩個已序數組A,B的中位數。所謂的中位數,對於一個有n個元素的已序數組,如果n是奇數,則中位數是第\frac{n+1}{2}個元素的值;如果n是偶數,則它的中位數是第\frac{n}{2}與第\frac{n}{2}+1數的平均值。對於m+n為奇數,則利用算法二求第\frac{n+m+1}{2}個元素的值即可,對於m+n為偶數,利用算法二求第\frac{m+n}{2}個與第\frac{m+n}{2}+1個元素的值,求其平均值即可。

  對於這個問題,在LeetCode中有另外一種解法,但是閱讀后發現其需要處理的個別case太多,相比而言沒有本文所介紹的算法簡潔。如果想要了解,給出鏈接:http://leetcode.com/2011/03/median-of-two-sorted-arrays.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM