leetcode | Median of Two Sorted Arrays 尋找2個有序陣列中第k大的值
There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays.
The overall run time complexity should be O(log(m + n)).
分析
本題更經典通用的描述方式時:
給定2個有序陣列,找出2個數組中所有元素中第k大的元素。
思路1
直觀思路就是類似merge-sort,將2個數組merge成一個數組,即可得到第k大的值,但是時間複雜度
思路2
然後我們僅需要第k大的元素,不需要排序這個複雜的操作:可以定義一個計數器m,表示找到了第m大的元素;同時指標pa,pb分別指向陣列A,B的第一個元素,使用merge-sort的方式,當A的當前元素小於B的當前元素時:pa++, m++
pb++, m++
。最終當m等於k時,就得到了第k大的元素。時間複雜度思路3
從題目中的要求
那麼有沒有更好的方案?我們可以考慮從k入手。如果我們每次能夠刪除一個一定處於第k大元素之前的元素,那麼需要進行k次。但是如果我們每次都能刪除一半呢?可以利用A,B有序的資訊,類似二分查詢,也是充分利用有序。
假設A 和B 的元素個數都大於k/2,我們將A 的第k/2 個元素(即A[k/2-1])和B 的第k/2個元素(即B[k/2-1])進行比較,有以下三種情況(為了簡化這裡先假設k 為偶數,所得到的結論對於k 是奇數也是成立的):
- A[k/2 - 1] == B[k/2 - 1];
- A[k/2 - 1] > B[k/2 - 1];
- A[k/2 - 1] < B[k/2 - 1];
如果A[k/2 - 1] < B[k/2 - 1] ,意味著 A[0] 到 A[k/2 - 1] 的元素一定小於 A+B 第k大的元素。因此可以放心的刪除A陣列中的這k/2個元素;
同理,A[k/2 - 1] > B[k/2 - 1];可以刪除B陣列中的k/2個元素;
當A[k/2 - 1] == B[k/2 - 1] 時,說明找到了第k大的元素,直接返回A[k/2 - 1] 或B[k/2 - 1]的值。
因此可以寫一個遞迴實現,遞迴終止條件是什麼呢?
- A或B為空時,直接返回A[k-1] 或 B[k-1]
- 當k = 1時,返回min(A[0], B[0]) //第1小表示第一個元素
- 當A[k/2 - 1] == B[k/2 - 1] 時,返回A[k/2 - 1] 或B[k/2 - 1]
C語言實現
static int find_kth(int* A, int m,int* B, int n, int k);
static int min(p, q) {return (p < q) ? p : q;}
double findMedianSortedArrays(int * nums1, int nums1Size, int* nums2, int nums2Size) {
int m = nums1Size;
int n = nums2Size;
int total = m+n;
int k = total/2;
if(total & 0x01) {
return find_kth(nums1, m, nums2, n, k+1); //奇數,返回唯一中間值
} else {
return (find_kth(nums1, m, nums2, n, k) + find_kth(nums1, m, nums2, n, k+1)) / 2.0; //偶數,返回中間2個的平均值
}
}
//找到A,B組合中第k小的值: AB[k-1]
int find_kth(int* A, int m,int* B, int n, int k) {
//假設m都小於n
if (m > n)
return find_kth(B, n, A, m, k);
if (m == 0)
return B[k-1];
if (k == 1) //終止條件
return min(A[0], B[0]);
int i_a = min(m, k/2);
int i_b = k - i_a;
if (A[i_a-1] < B[i_b-1])
return find_kth(A+i_a, m-i_a, B, n, k-i_a);
else if (A[i_a-1] > B[i_b-1])
return find_kth(A, m, B+i_b, n-i_b, k-i_b);
else
return A[i_a-1];
}