給定兩個大小爲 m 和 n 的有序數組 nums1 和 nums2。
請你找出這兩個有序數組的中位數,並且要求算法的時間複雜度爲 O(log(m + n))。
你可以假設 nums1 和 nums2 不會同時爲空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
則中位數是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
則中位數是 (2 + 3)/2 = 2.5
思路一
對於這樣的題很自然的想法是:先合併,再求中位數。可是仔細想想似乎不滿足題幹對時間複雜度的要求,因爲合併所需要的時間是O(N)
,其中N = m + n
,且在空間上也會有額外的消耗。顯然這不是最優的解法。(不過後來翻評論發現有哥們這樣做,而且基本都過了???)
解法一
可以參考歸併排序的merge
部分,這裏就不做探討了。
思路二
很慚愧,翻了題解~
值得一說的是,筆者發現官方對這道題的解法數學味有點濃,於是花了點時間研究了一波,下面是一些收穫:
先明確一下概念:中位數把一組有序的數列劃分成兩個數量級相當的子集,左子集恆小於等於右子集。
什麼意思呢?翻譯成僞代碼就是:
COUNT(Left) == COUNT(Right)
;ANY(Left) <= ANY(Right)
;
咋一看好像沒什麼卵用,仔細一想其實不然,這個概念是可逆的,也就是說:如果某個數滿足這個概念,則其就是要找的中位數。
那麼不妨假設在第一個有序數集A
中找到了一個下標爲x
的數把A
劃分爲等量的兩份,在數集B
中找到的劃分點下標爲y
,畫圖形象化就是:
|
A[0]...A[x-1] | A[x]...A[m-1]
B[0],B[1]...B[y-1] | B[y],B[y+1]...B[n-1]
|
搞清楚了這個,就可以按圖索驥了:
第一步、根據第一個等量關係可以推導出:
- 當
m + n
爲偶數時,(COUNT(A.Left) + COUNT(B.Left) = x + y) == (COUNT(A.Right)+COUNT(B.Right) = m - x + n - y)
- 當
m + n
爲奇數時,(COUNT(Left) = x + y) == (COUNT(Right) = m - x + n - y + 1)
也就是說:$ 2(x+y) = m + n + (m + n)%2 y = {{ m + n + {(m + n)} % 2 } \over 2} - x $
函數形如:
第二步、利用第一步分析結果:枚舉一個x
就可以得到一個y
,這個時候還需要滿足條件2:
由於A
和B
初始是有序的: A[x-1] <= A[x],B[y-1] <= B[y]
;
此時若A[x-1] <= B[y] 且 B[y-1] <= A[x]
,則就相當於滿足了條件2。
第三步、找到x
和y
後如何得到最終的中位數呢?
通過舉例的方式很快可以發現:
- 當
m + n
爲奇數時,ans = Max(A[x-1], B[y-1])
; - 當
m + n
爲偶數時,ans = (Max(A[x-1], B[y-1]) + Min(A[x], B[y]))/2
;
解法二
class Solution {
public double findMedianSortedArrays(int[] A, int[] B) {
int m = A.length, n = B.length;
if (m > n) { // 保證 m <= n, 確保j>=0
int t = m;
int[] T = A;
m = n;
A = B;
n = t;
B = T;
}
int a = 0, b = m, len = m + n;
int halfLen = (len + (len) % 2) / 2;
int x, y;
while (a <= b) {
x = a + (b - a) / 2;
y = halfLen - x;
if (x > a && A[x - 1] > B[y]) { // x取得太大了
b = x - 1;
} else if (x < b && A[x] < B[y - 1]) { // x取得太小了
a = x + 1;
} else { // x,y滿足了條件2
int left;
if (x <= 0) {
left = B[y - 1];
} else if (y <= 0) {
left = A[x - 1];
} else {
left = Math.max(A[x - 1], B[y - 1]);
}
if ((len) % 2 == 1) {
// m+n爲奇數
return left;
}
int right;
if (x >= m) {
right = B[y];
} else if (y >= n) {
right = A[x];
} else {
right = Math.min(A[x], B[y]);
}
// m+n爲偶數
return (left + right) / 2.0;
}
}
return 0.0;
}
}
其實可以發現,這個代碼好像跟官方的解答一毛一樣哎~