假設有長度分為為M和N的兩個升序數組A和B,在A和B兩個數組中尋找第K大的數,即將A和B按升序合并後的第K個數。
解法一:
使用兩個指標指向A和B的開頭,很容易在O(M+N)的時間內完成,此演算法略過。
解法二:
使用二分的方法。演算法思想在代碼注釋中
#include <iostream>#include <string.h>#include <stdlib.h>using namespace std;//Notice : K > 0int FindKthElm(int A[], int aBeg, int aEnd, int B[], int bBeg, int bEnd, int k){if (aBeg > aEnd){return B[bBeg + k - 1];}if (bBeg > bEnd){return A[aBeg + k - 1];}//取中間位置int aMid = aBeg + (aEnd - aBeg)/2;int bMid = bBeg + (bEnd - bBeg)/2;//從A和B的開始位置到兩個數組中間位置的元素個數int halfLen = aMid - aBeg + bMid - bBeg + 2;if (A[aMid] < B[bMid]){if (halfLen > k){// 此時在合并的數組中A[aBeg...aMid]和元素一定在B[bMid]的左側,// 即此時第k大的元素一定比B[bMid]這個元素小(嚴格來說不大於)// 故以後沒有必要搜尋 B[bMid...bEnd]這些元素return FindKthElm(A, aBeg, aEnd, B, bBeg, bMid - 1, k);}else{// 此時在合并的數組中A[aBeg...aMid]元素一定在B[bMid]的左側,// 所以前K個元素中一定包含A[aBeg...aMid](可以使用反證法來證明這點)。// 但是無法判斷A[amid+1...aEnd]與B[bBeg...bEnd]之間的關係,幫需要對他們進行判斷// 此時K就剩下除去A[aBeg...aMid]這些元素,個數為k - (aMid - aBeg + 1)return FindKthElm(A, aMid + 1, aEnd, B, bBeg, bEnd, k - (aMid - aBeg + 1));}}else{//注釋與上面相似if (halfLen > k){return FindKthElm(A, aBeg, aMid - 1, B, bBeg, bEnd, k);}else{return FindKthElm(A, aBeg, aEnd, B, bMid + 1, bEnd, k - (bMid - bBeg + 1));}}}int main(){const int ALen = 11;const int BLen = 5;int apos = 0;int bpos = 0;int A[ALen];int B[ALen];//產生兩個遞增數組A 和 Bfor (int i = 1; i <= ALen + BLen; ++i){if (apos >= ALen){B[bpos++] = i;}else if (bpos >= BLen){A[apos++] = i;}else{if (rand()%2 == 1){A[apos++] = i;}else{B[bpos++] = i;}}}//輸出A和B的內容for (int i = 0; i < ALen; ++i){cout <<A[i] <<" ";}cout <<endl;for (int i = 0; i < BLen; ++i){cout <<B[i] <<" ";}cout <<endl;//驗證每個K是不是正解for (int i = 1; i <= ALen + BLen; ++i){cout << i <<" : "<<FindKthElm(A, 0 , ALen - 1, B, 0 , BLen - 1, i)<<endl;}return 0;}