兩個排序數組的中位數
Median of Two Sorted Arrays
算法
給定兩個大小爲 m 和 n 的有序數組 nums1 和 nums2 。
請找出這兩個有序數組的中位數。要求算法的時間複雜度爲 $O(\log (m+n))$ 。數組
示例 1:函數
nums1 = [1, 3] nums2 = [2] 中位數是 2.0
示例 2:code
nums1 = [1, 2] nums2 = [3, 4] 中位數是 (2 + 3)/2 = 2.5
Tags: 數組,二分查找,分治算法排序
題目給定兩個有序的數組,找出中位數並不困難,最簡單的一個循環就行,時間複雜度 $O(m+n)$ 。可是題目要求時間複雜度爲 $O(\log (m+n))$ 就是一個大問題,具體思路採用二分查找,以下:遞歸
其中k 表示要求的中位數的位置,初始化 $k = (m + n + 1) / 2$ 和 $k = (m + n + 2) / 2$,即 $m + n$ 是偶數時爲中間兩個, $m + n$ 是奇數時爲中位數的後面一個。ip
首先將 A 劃分爲左右兩部分,其中 $i = k / 2$ ,$length(left_A) = i,length(right_A) = k - i$leetcode
left_A | right_A A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]
而後將 B 劃分爲左右兩部分,其中 $j = k / 2$ ,$length(left_A) = j,length(right_A) = k - j$get
left_B | right_B B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
則,A、B 數組可分爲左右兩部分,以下:io
left | right A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1] B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
劃重點來了
若是,$A[i-1] > B[j-1]$,則說明,中位數必定在 $left_A、right_A、right_B$中,這樣就排除了$left_B$,想一想這是爲何?
由於有$A[i-1] > B[j-1]$ 不妨設 $A[0] > B[p],p=0,1,2,\ldots,j-1$ 則中位數必定不在$left_B$中,因此縮小查找範圍。
left | right A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1] | B[j], B[j+1], ..., B[n-1]
若是,$A[i-1] < B[j-1]$,則說明,中位數必定在 $right_A、left_B、right_B$中,這樣就排除了$left_A$,想一想這是爲何?理由同上。
left | right | A[i], A[i+1], ..., A[m-1] B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
接下來,將縮小範圍的新數組 A 或者 B 按一樣的劃分,一樣的縮小範圍,就可獲得最後結果。
由上可得,方法爲二分查找,定義遞歸函數,肯定遞歸函數的出口爲:
如何肯定奇數個數取中位數,偶數個數取中間兩個數的平均數,我想,利用整數除法的規則,很容易就獲得 中位數爲 (m + n + 1) >> 1
這個數與 (m + n + 2) >> 1
取平均值便可,當個數爲奇數時,兩個數就是中間一個數。
class Solution { public: double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { int m = nums1.size(); int n = nums2.size(); int l = (m + n + 1) >> 1; int r = (m + n + 2) >> 1; int* ln = m > 0 ? &nums1[0] : nullptr; int* rn = n > 0 ? &nums2[0] : nullptr; return ( getKth(ln, m, rn, n, l) + getKth(ln, m, rn, n, r) ) / 2.0; } int getKth(int* ln, int m, int* rn, int n, int k) { if (m > n) { return getKth(rn, n, ln, m, k); } if (m == 0) { return rn[k - 1]; } if (k == 1) { return min(ln[0], rn[0]); } int i = min(m, k / 2), j = min(n, k / 2); if (ln[i - 1] > rn [j - 1]) { return getKth(ln, m, rn + j, n - j, k - j); } else { return getKth(ln + i, m - i, rn, n, k - i); } } };