1. 程式人生 > >從倆個有序陣列中找出第K小的數。要求時間複雜度O(logmin(m,n))

從倆個有序陣列中找出第K小的數。要求時間複雜度O(logmin(m,n))

思路

  該題目要求時間複雜度為O(log(min{m,n})) 所以不能直接遍歷任意一個數組這樣時間複雜度就不符合了。也不能對任意一陣列進行二分查詢,因為要求是倆個數組元素合併後的第K小的數,所以直接遍歷用二分遍歷任意一個數組也是行不通的。
  故我們可以以區間的方式解決這個問題。我們先假設第一個陣列個數小於等於第二個陣列個數。如果不是則交換倆個數組即可。這樣的話,先定義倆個區間,第一個區間range大小為min(2/k,len1),這個區間最大為k/2,最小為1.第二個區間就是K-range大小。

  我們可以通過檢視第一個區間的最後一個元素和第二個區間的最後一個元素的值大小來縮短區間最終找到第k個元素這分三種情況:
  1.如果第一個區間最後一個元素的值等於第二個區間最後一個元素的值。那麼區間的最後一個元素就是第k個元素。因為這倆個區間個數加起來等於k,並且這倆個區間值又是相等的,那麼說明我們篩選出來了前K小的元素區間,所以最後一個元素是第k小的元素。
  2.如果第一個區間最後一個元素值比第二個區間最後一個元素值小。那麼說明第一個區間所有元素都是前k小區間元素中的元素。所以我們可以繼續遍歷,此時不在查詢第k小個數了,更新到了查詢第k-range1小的數了。那麼我們可以縮短第一個陣列,第二個陣列不變繼續遍歷。
  3.如果第二個區間最後一個元素值比第義個區間最後一個元素值小。那麼說明第二個區間所有元素都是前k小區間元素中的元素。所以我們可以繼續遍歷,此時不在查詢第k小個數了,更新到了查詢第k-range2小的數了。那麼我們可以縮短第二個陣列,第一個陣列不變繼續遍歷。

  那麼當有一個數組個數被篩選的縮短至0時,答案就出來了,此時第k個元素必定在第二個陣列直接就可以找到了。
  如果k縮小到1了,答案也出來了,那就是倆個縮短後的陣列中的首元素的較小值為第k個元素。
  所以這就是以上思路。

程式碼

int FindKthNum(int * arr1, int len1, int*arr2, int len2, int kth)
{
    assert(arr1 != NULL&&arr2 != NULL);
    assert(len1 + len2 > kth);
    if (kth == 1) return min(arr1[0], arr2[0]);
    if (len1 > len2) return FindKthNum(arr2, len2, arr1, len1, kth);
    if (len1 == 0) return
arr2[kth];//為了後續遞迴時,如果第一個區間篩選完了,那麼結果肯定就在第二個區間了 int range = std::min(len1, kth / 2); int range2 = kth - range; if (arr1[range - 1] == arr2[range2 - 1]) return arr1[range - 1]; if (arr1[range - 1] < arr2[range2 - 1]) return FindKthNum(arr1 + range, len1 - range, arr2, len2, kth - range
); if (arr2[range2 - 1] < arr1[range - 1]) return FindKthNum(arr1, len1, arr2+range2, len2 - range2, kth - range2); }