1. 程式人生 > >求兩個已排序的陣列中所有元素的第K大(小)

求兩個已排序的陣列中所有元素的第K大(小)

1.reference

2.解題思路

以下均假設A[0…m-1],B[0…n-1];

1)O(n)
假設A,B均以降序排列,宣告兩個指標p,q。p指向A[0], q指向B[0]。再來一個count=0,用來表示當前已經到第count大了。然後指標開始遊,如果*p > *q,則p++, count++;如果*p < *q,則q++, count++;而如果相等,兩個遊標選一個走即可,同時count++。最後當count==k時,就找到了所有元素的第K大了。

2)O(log(n))
上面O(n)的做法是一個一個剪,而這裡,就是一半一半地剪。為了方便理解,這裡就是求兩個陣列的第K小元素(原理是一樣的,第K小即為第(m+n-k)大嘛),且A,B均以升序排列。因為A, B陣列均已排好序,所以有如下結論(假設K為偶數,奇數同理)。
1’ A[] 或 B[] 中有個為空,則直接B[k-1]或A[k-1];
2’ 假設m,n>=k/2 (如果不滿足,則下標取自己的上限即可)

  • 1.A[k/2-1] < B[k/2-1]:刪除A[0…k/2-1],第k小肯定不在裡面。因為AUB之後,A[0…k/2-1]內的所有元素均在B[k/2-1]此元素之前,而兩組資料個數總和才為k個。
  • 2.A[k/2-1] > B[k/2-1]:刪除B[0…k/2-1],第k小肯定不在裡面。理由同上。
  • 3.A[k/2-1] = B[k/2-1]:找到了第k小,即為A[k/2-1]=B[k/2-1]。

因此,由上述條件我們可以推出利用遞迴函式解決問題是極為方便的。遞迴過程及結束條件如下所示(設m < n,暫不防禦型程式設計)

  • 1.如果m=0,則return B[k-1];
  • 2.如果k=1,則return min(A[0], B[0]);
  • 3.m可能是小於k/2的,所以依上面分析取自己上限m即可,即有
    若A[min(m, k/2)-1] < B[k-min(m, k/2)-1]:刪除A[…],代入遞迴函式繼續。
    若A[min(m, k/2)-1] > B[k-min(m, k/2)-1]:刪除B[…],代入遞迴函式繼續。
    若A[min(m, k/2)-1] = B[k-min(m, k/2)-1]:找到,返回其中一個即可。

3.原始碼

#include <iostream>
#include <algorithm>
using namespace std;

class MySolution
{
public
: int find_k_min(int A[], int m, int B[], int n, int k) { if (m > n) return find_k_min(B, n, A, m, k); if (m == 0) return B[k-1]; if (k == 1) return min(A[0], B[0]); int pa = min(k/2, m); int pb = k - pa; if (A[pa-1] < B[pb-1]) find_k_min(A + pa, m - pa, B, n, k - pa); else if (A[pa-1] > B[pb-1]) find_k_min(A, m, B + pb, n - pb, k - pb); else return A[pa - 1]; } }; int main(int argc, char const *argv[]) { /* code */ int A[] = {1, 3, 5, 7, 9}; int B[] = {2, 4, 6, 8, 10}; MySolution test; cout << test.find_k_min(A, 5, B, 5, 5) << endl; return 0; }

執行結果就不貼了,心算即可驗證:)