LeetCode 尋找兩個有序數組的中位數

尋找兩個有序數組的中位數


題目來源:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/submissions/python

題目


給定兩個大小爲 m 和 n 的有序數組 nums1 和 nums2。算法

請你找出這兩個有序數組的中位數,而且要求算法的時間複雜度爲 O(log(m + n))。segmentfault

你能夠假設 nums1 和 nums2 不會同時爲空。數組

示例 1:bash

nums1 = [1, 3]
nums2 = [2]

則中位數是 2.0

示例 2:微信

nums1 = [1, 2]
nums2 = [3, 4]

則中位數是 (2 + 3)/2 = 2.5

解題思路


  1. 使用方法:遞歸;
  2. 本題數組有序,要求中位數,但可將本題轉換爲求第 k 個最小數。若爲奇數,則第 k 位所在的數就是中位數;若爲偶數,則第 k 小的數與第 k + 1 小的數之和的一半就是中位數;
  3. 由於數組是有序的,兩個數組比較求第 k 小的數,不需逐個比較,一個個剔除。能夠考慮一半一半的排除,每次排除 k/2 個數字。
  4. 每次比較兩個數組中第 k/2 位數(當 k 爲奇數時,向下取整),當出現其中一個數組的值較小時,說明這個數組包括第 k/2 位數以及前面的數字都比第 k 位數小,直接剔除;此時 k 也要減去剔除數字的個數,遞歸執行這個步驟;
  5. 遞歸跳出的條件,當其中一個數組爲空的時候,或者當 k1 的時候;
  6. k1 的時候,這時求第 1 小的數,只要比較兩個數組首位數字較小便是所求的中位數;
  7. 當數組爲空,且 k 不等於 1 時,這個時候,取其中不爲空數組的第 k 小的數。

例子 1:spa

nums1: [1, 3, 5]
nums2: [1, 2, 3, 4, 5, 6, 7, 8]

這個例子中,假設要求的是第 6 小的數字。比較 k/2 也就是第 3 個數。code

nums1 第 3 個數字爲 5,nums2 第 3 個數字爲 35 > 3,因此將下面數組的 3 個數字剔除掉,變成比較 [1, 3, 5][4, 5, 6, 7, 8],這個時候剔除 3 個數字,因此 k 需減去 3,此時 k=3。繼續比較;blog

k/2 不能整除,向下取整,爲 1,比較兩個數組第一個數字,此時 1<4,剔除第一個數組的 1,剩下 [3, 5][4, 5, 6, 7, 8]k3-1=2,再次比較;遞歸

k/213<4,剔除第一個數組的 3,剩下 [5][4, 5, 6, 7, 8]k2-1=1

此時 k=1,也就是求第 1 小的數字,這個時候比較剩下兩個數組的首位數字,去較小的數字即爲第 1 小的數字,也就是 4

例子 2:

這裏考慮數組長度小於 k/2 的狀況:

nums1: [1, 2]
nums2: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

一樣求第 6 小的數。

解題思路如 例子 1,先比較第 k/2 位數的大小,k/2=3。因爲第一個數組長度小於 3,這時直接取最末尾的數字 2,而第二個數組取第 3 位數,也就是 3,這裏 2<3,直接剔除第一個數組的兩個數字,k 則變爲 6-2=4

因爲此時第一個數組爲空,因此這個時候直接取另一個數組第 k 小,也就是第 4 小數,也就是 4,便是原本要求的兩個數組中第 6 小的數字。

由於每次循環都會減小 k/2 個元素,因此時間複雜度是O(log(k)),而 k 在本題中就是 (m+n)/2,因此最終複雜度就是 O(log(m+n))

代碼實現


class Solution:
    def findMedianSortedArrays(self, nums1, nums2) -> float:
        m = len(nums1)
        n = len(nums2)
        # 這裏默認爲偶數狀況,若爲奇數,則計算兩次相同的 k 值
        med_left = (m + n + 1) // 2
        med_right = (m + n + 2) // 2

        return (get_k_num(nums1, 0, m-1, nums2, 0, n-1, med_left) + get_k_num(nums1, 0, m-1, nums2, 0, n-1, med_right)) / 2

def get_k_num(nums1, head1, end1, nums2, head2, end2, k):
        len1 = end1 - head1 + 1
        len2 = end2 - head2 + 1
        # 遞歸跳出條件
        if (len1 == 0):
            return nums2[head2 + k - 1]
        if (len2 == 0):
            return nums1[head1 + k - 1]
        if (k == 1):
            return min(nums1[head1], nums2[head2])
        
        i = head1 + min(len1, k // 2) - 1
        j = head2 + min(len2, k // 2) - 1

        if (nums1[i] > nums2[j]):
            return get_k_num(nums1, head1, end1, nums2, j + 1, end2, k - (j - head2 + 1))
        
        else:
            return get_k_num(nums1, i + 1, end1, nums2, head2, end2, k - (i - head1 + 1))

實現效果


find_median_result.jpg


以上就是本篇的主要內容。

吐槽: 本篇幅解題思路寫下來,回頭看的時候,發現純文字感受邏輯會有點亂,下次爭取結合圖例完善這部分的內容。


歡迎關注微信公衆號《書所集錄》
相關文章
相關標籤/搜索