[LeetCode] 4. Median of Two Sorted Arrays 題解

問題描述

兩個有序的數組 nums1nums2 ,它們的數組長度分別爲 m 和 n。要求找到這兩個數組的中位數,且整體的時間複雜度必須爲 O(\log(m+n))python

假設 nums1nums2 都不爲空。算法

例 1:數組

nums1 = [1, 3]
nums2 = [2]
中位數爲 2.0
複製代碼

例 2:bash

nums1 = [1, 2]
nums2 = [3, 4]
中位數爲 (2 + 3)/2 = 2.5
複製代碼

問題難度

Hardide

解題思路

作第一遍時,使用的遍歷的辦法,即按照從小到大的順序,分別遍歷兩個隊列,一直遍歷到 (m+n)/2 的位置,便是咱們所要的中位數,當時心想,這也太簡單了吧,Hard 級別也不過如此。spa

再仔細看題目,它還有個時間複雜度爲 O(\log(m+n)) 的要求,而個人方案的時間複雜度卻爲 O(n),並不知足題目條件,可見,單這個條件就值 Hard 級別了。code

解這道題的關鍵並非高超的算法,而是心中要有一副這樣的圖:隊列

left side       |  right side 
nums1:   A(0),A(1),...,A(i-1) | A(i),...,A(m-1)
nums2:   B(0),B(1),...,B(j-1) | B(j),...,B(n-1)
複製代碼

咱們把兩個數組當作一個總體,有一根豎線將其中的元素從中間分開,且左邊的部分和右邊部分的元素相同(總數爲奇數狀況下,左邊比右邊多 1 個元素),那麼當 m+n 爲偶數時,中位數爲 [max(A(i-1),B(j-1)) + min(A(i)+B(j))]\div2 ,當 m+n 爲奇數時,中位數爲 max(A(i-1),B(j-1))leetcode

咱們都知道,左邊的元素爲 i+j 個,而左右兩邊元素相同,則get

i + j = \frac{m+n+1}{2}

咱們能夠用 i 來表示 j,則

j = \frac{m+n+1}{2} - i

因此,該題就變成了,在數組 A 中尋找一個 i,使得 A(i) \ge B(j-1),且 B(j) \ge A(i-1)​ 成立,這兩個不等式的含義是,豎線右邊最小的數必定不比左邊最大的數小,知足該條件,咱們就能夠說找到了這個豎線。

咱們在找 i 的過程當中,不免會碰到 A(i) < B(j-1) 時候,此時咱們須要將 i 向右移,使 A(i) 增大,當 i 右移,i 增大的同時,j 會減小,即 B(j-1) 的值會變小,這樣操做 i 以後,會讓咱們更接近目標;同理,當 B(j) < A(i-1) 時,咱們須要將 i 向左移。

經過上面的分析,咱們最終可使用二分查找法來尋找這個 i 值,又因爲二分查找的時間複雜度爲 O(\log(n))​,這種方法能夠知足題目的要求。

思路說完了,下面來講下該題目的邊界條件,因爲 j 是經過減去 i 算出來的,而 i 的最大值爲 m(A 全在左邊時),因此爲了使 j 不爲負數,數組 A 須要爲兩個數組中,元素數較少的那個。

當 i 爲 0 時,數組 A 全在右邊,咱們只須要判斷 A(i) \ge B(j-1)​ 成立;當 i 爲 m 時,數組 A 全在左邊,只需判斷 B(j) \ge A(i-1)​ 成立

同理當 j 爲 0 時,數組 B 全在右邊,咱們只需判斷 B(j) \ge A(i-1) 成立;當 j 爲 n 時,數組 B 全在左邊,只需判斷 A(i) \ge B(j-1) 便可

因而,咱們能夠寫出下面的代碼:

class Solution(object):
    def findMedianSortedArrays(self, nums1, nums2):
        m, n = len(nums1), len(nums2)
        if m > n:
            m, n, nums1, nums2 = n, m, nums2, nums1

        if m == 0 and n == 0:
            return None

        begin = 0
        end = m
        i = j = 0
        while True:
            i = (begin + end) / 2
            j = (m + n + 1) / 2 - i

            if (i == 0 or j == n or nums2[j] >= nums1[i-1]) and\
                    (i == m or j == 0 or nums1[i] >= nums2[j-1]):
                left_max = 0
                if i == 0: left_max = nums2[j-1]
                elif j == 0: left_max = nums1[i-1]
                else: left_max = max(nums1[i-1],nums2[j-1])
                
                if (m+n)%2 != 0:
                    return left_max

                right_min = 0
                if i == m: right_min = nums2[j]
                elif j == n: right_min = nums1[i]
                else: right_min = min(nums1[i], nums2[j])

                return (left_max + right_min)*1.0/2

            elif j < n and i > 0 and nums2[j] < nums1[i-1]:
                end = i - 1
            elif j > 0 and i < n and nums1[i] < nums2[j-1]:
                begin = i + 1
複製代碼

這道題直接看代碼是很難理解的,但若是心中有前文說的那張圖,即可以沿着思路慢慢化解,可見要達到題目的要求並不簡單,Hard 難度名不虛傳。

原題連接

相關文章
相關標籤/搜索