1. 程式人生 > >leetcode | Median of Two Sorted Arrays 尋找2個有序陣列中第k大的值

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大的值,但是時間複雜度O(m+n);

思路2

然後我們僅需要第k大的元素,不需要排序這個複雜的操作:可以定義一個計數器m,表示找到了第m大的元素;同時指標pa,pb分別指向陣列A,B的第一個元素,使用merge-sort的方式,當A的當前元素小於B的當前元素時:pa++, m++

,當*pb < *pa時,pb++, m++。最終當m等於k時,就得到了第k大的元素。時間複雜度O(k),但是當k接近於m+n時,複雜度還是O(m+n);

思路3

從題目中的要求O(log(m+n))可以聯想到肯定要用到二分查詢的思想

那麼有沒有更好的方案?我們可以考慮從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]; }