Google、Microsoft、Uber、Apple 同時考了這道題以後,收到大批反饋信息要求查看最優題解,秋招大門已經打開,你還在等什麼?面試
題目描述
兩個已排好序的數組,找出二者合併後的數組的中位數
Example
給出 A=[1,2,3,4,5,6] 和 B=[2,3,4,5],他們合併後的中位數是 3.5((3+4)/2)
給出 A=[1,2,3] 和 B=[4,5],他們合併後的中位數是 3
算法分析算法
1. 這道題的直觀解法:就是掃一遍兩個數組,找到中間位置的那一個/兩個數,而後獲得中位數。時間複雜度是 O(m+n),其中 m 和 n 分別是數組 A 和數組 B 的長度數組
2. 可是!面試官會這麼輕易就放過你嗎?顯然是不可能滴~~我偷看了一下題目描述下的「challenge」標籤,原來這道題的最優解是 O(log(m + n)) 的複雜度。(m + n) 是倆數組合並後的總長度 L,看到 O(log L) 這樣的複雜度,並且仍是有序數組,能想到哪一個算法嗎?沒錯,就是二分查找!那咱們試試bash
a. 若是我取出 A[i] ,用二分查找在數組 B 中找到 A[i] 能夠插入的位置,假設 A[i] 在 B 中的插入位置是 j,那麼 A[i] 在整個合併數組中的位置就是 (i + j) ,由於要求的中位數的位置是 (m + n) / 2,經過比較 (i + j) 和 (m + n) / 2 的大小能夠每次捨棄 A 的一部分,從而收斂數組 A。用一樣的方法能夠收斂數組 B。可是這樣的複雜度是 O(log m * log n),複雜度大於 O(log(m + n)),顯然不是最優的測試
b. 那若是不是直接使用二分查找算法,而是借用二分查找的思想呢?就是每次有選擇地捨棄掉數組的一部分,從而達到收斂數組縮小查找範圍的效果ui
i. 咱們從新看題目,要找中位數,就是要找第 k 大的數(k = (L / 2 + 1),其中L 是上面提到的合併後新數組的長度,當 L 是偶數時,要求第 (L / 2) 大和第 (L / 2 + 1) 大的兩個數)。當咱們捨棄掉一部分,假設捨棄部分的長度爲 length,那麼接下來就是在剩下的數組裏求第 (k - length) 大的數。逐層縮小範圍,直到兩數組其中一個走完,或者要求的是第 1 大的元素,就能夠直接返回結果了spa
ii. 那如何「選擇」要捨棄哪部分呢?既然是要找合併後的數組 C 的第 k 大元素,即 C[k-1],那若是咱們從 A 和 B 中分別取前 k/2 個元素,其中必然有一部分是是在數組 C 的前 k 個數裏。設 mid = k / 2,當 A[mid - 1] < B[mid - 1] 時,能夠判定 A 的前 mid 個元素是在 C 的前 k 個數裏(此處可用反證法得證),那麼咱們則捨棄 A 的前 mid 個元素。反之則捨棄 B 的前 mid 個元素。如今數組 A 或者 B 已經捨棄掉 k/2 個元素,縮小查找範圍了,那接下來能夠按照一樣的方法繼續選擇嗎?固然!如今剩下總共 (L - mid) 個元素,且 A 和 B 依舊有序,要找的是第 (k - mid) 大的元素,因此咱們能夠按照上面的方法繼續遞歸選擇下去,直到找到目標元素!3d
複雜度分析:每次從合併後數組 C 裏減小 k/2 個元素,直到找到目標元素。因此時間複雜度是 O(log L) = O(log (m + n)) !code
參考程序orm
class Solution:
""" @param A: An integer array. @param B: An integer array. @return: a double whose format is *.5 or *.0 """
def findMedianSortedArrays(self, A, B):
m, n = len(A), len(B)
if m == 0 and n == 0:
return 0.0
total = m + n
if total % 2 == 1:
return self.kth_largest(A, B, total / 2 + 1) * 1.0
else:
a = self.kth_largest(A, B, total / 2)
b = self.kth_largest(A, B, total / 2 + 1)
return (a + b) / 2.0
def kth_largest(self, A, B, kth):
m, n = len(A), len(B)
if m == 0:
return B[kth-1]
if n == 0:
return A[kth-1]
if kth == 1:
return min(A[0], B[0])
mid = kth / 2
a, b = float("inf"), float("inf")
if m >= mid:
a = A[mid - 1]
if n >= mid:
b = B[mid - 1]
if a < b:
return self.kth_largest(A[mid:], B, kth - mid)
else:
return self.kth_largest(A, B[mid:], kth - mid)複製代碼
面試官角度分析
這道題是二分方法的拓展運用,而且須要對二分有比較高的理解,好比怎麼樣經過猜想試一試二分來解決問題。若是可以達到二分的方法,固然必然是Strong Hire的等級