(C/C++)給定兩個長度為m和n的有序列表,以O(logm+logn)複雜度找出有序列表第k小的數
給定兩個長度為m和n的有序列表,以O(logm+logn)複雜度找出有序列表第k小的數
思路
logm+logn即可推斷是分治,每次折半得到兩個陣列的A[midA]和B[midB],有:
-
A[midA]>B[midB]時:
- 若midA+midB(實際上是midA+midB-beginA-beginB+2,下同)的元素個數>k,則midB之後的序列肯定不包含在最終的有序k個元素的數列中。不能包括=k的情況,因為=k時,midB也可能包含在最終的有序k個元素數列中。這裡的等於號的邏輯錯誤看了很久才發現。
- 若midA+midB的元素個數<=(注意是小於等於)k,則midA及之前的元素肯定包含在最終的有序k個元素的數列中 ,k=k-(midA-beginA+1),轉換為一個更小的求第k個數的問題。
#include <iostream> #define M 5 #define N 6 using namespace std; int INDEX1[M] = { 1,2,4,5,6 }; int INDEX2[N] = { 2,3,5,7,9,10 }; int FindK(int*A, int*B, int beginA, int endA, int beginB, int endB, int k) { printf("A:%d-->%d B:%d-->%d\n", beginA, endA, beginB, endB); int midA = (beginA + endA) / 2; int midB = (beginB + endB) / 2; //已經確定了其中一個有序序列 //剩下的有序序列取第k小的數即可 if (beginA > endA ) { cout << "K:" << k << endl; return B[beginB + k - 1]; } if (beginB > endB ) { cout << "K:" << k << endl; return A[beginA + k - 1]; } //beginA、beginB是已確定的兩個範圍 //len是從mid到begin的元素個數 int len = midA - beginA + midB - beginB + 2; if (A[midA] < B[midB]) { if (len <= k){ //因為折半後合併的元素個數小於K //midA之前的元素一定包含在最終求得第K小的數的有序數列中 //但midB前的元素可能大於a[midA],無法確定 return FindK(A, B, midA + 1, endA, beginB, endB, k - (midA - beginA + 1)); } else //因為合併的元素個數大於所求K //midA後的元素可能小於midB,但midB後的元素肯定大於midB,不可能包含在最終的有序數列中 return FindK(A, B, beginA, endA, beginB, midB - 1, k); } else{ if (len <= k) return FindK(A, B, beginA, endA, midB + 1, endB, k - (midB - beginB + 1)); else return FindK(A, B, beginA, midA - 1, beginB, endB, k); } } //實際這裡的邊界情況判斷並不需要 int Find(int*A, int*B, int beginA, int endA, int beginB, int endB, int k) { if (B[0] >= A[M - 1]) { //cout << "B[0] >= A[M]"; if (k <= M) return A[k - 1]; return B[k - M - 1]; } if (A[0] >= B[N - 1]) { //cout << "A[0] >= B[N]"; if (k <= N) return B[k - 1]; return A[k - N - 1]; } if (k <= M && B[0] >= A[k - 1]) return A[k - 1]; if (k <= N && A[0] >= B[k - 1]) return B[k - 1]; return FindK(A, B, beginA, endA, beginB, endB, k); } void showArray(int a[], int length) { for (int i = 0; i < length; i++) { cout << a[i] << " "; } cout << "\n"; } int main(void) { cout << "INDEX1:"; showArray(INDEX1, M); cout << "INDEX2:"; showArray(INDEX2, N); int k; cin >> k; cout << "第" << k << "小的數是:" << Find(INDEX1, INDEX2, 0, M - 1, 0, N - 1, k); system("pause"); return 0; }