LeetCode 4.尋找兩個正序數組的中位數


題目:


給定兩個大小為 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


思路:

思路:

看示例得知,中位數的概念是奇數情況:為中間位;偶數情況為中間2位之和平均數

python 中的% 和/ 的意思

%取余數

/取整數,

本題的難點是限制時間復雜度為 O(log(m + n))

方法一:

一般解法,先不考慮時間復雜度要求,如果除法運算想保留小數點,被除數保留小數點即可,這樣就會有完整結果。

class Solution(object):
    def findMedianSortedArrays(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: float
        """
        tmp = []
        result = 0
        for i in nums1:
            tmp.append(i)
        for n in nums2:
            tmp.append(n)
        tmp.sort() #排序
        p = len(tmp)%2
        if p ==0: #偶數
            result = (tmp[len(tmp)/2-1]+tmp[len(tmp)/2])*/2.0 
        else: #奇數
            result = tmp[(len(tmp)+1)/2-1]
        return result

執行用時 :48 ms, 在所有 Python 提交中擊敗了76.69%的用戶

內存消耗 :12.8 MB, 在所有 Python 提交中擊敗了11.11%的用戶

或者是選用合並再sort,代碼更簡潔些。

class Solution(object):
    def findMedianSortedArrays(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: float
        """
        nums = nums1 + nums2
        nums.sort()
        count = len(nums)
        if count % 2 == 0:
            return (nums[int(count / 2 - 1)] + nums[int(count / 2)]) / 2.0
        else:
            return nums[int(count / 2)]

方法二:

想不出來關於時間復雜度的方法實現,這里摘自一個題解大大的思路(作者:louis-38),我覺得很棒,從另一個角度分析了二分法

在統計中,中位數被用來:

將一個集合划分為兩個長度相等的子集,其中一個子集中的元素總是大於或等於另一個子集中的元素。
和中位數無關的元素

假設一個集合A被中位數划分成兩個長度相等的子集A1和A2,A1中的元素總是小於或等於A2中的元素。

如上圖所示的集合A,其中位數是4.5。

當我們同時從A1和A2中移除任意n個元素(不移除A1最大元素和A2最小元素),集合A的中位數是不變的。

即我們移除A1中的1,2,3這三個元素中的任意n個,同時移除A2中的6,7,8這三個元素的任意n個,集合A的中位數還是4.5。
所以我稱1,2,3,6,7,8這幾個元素是和中位數無關的元素。即對稱地移除他們時,對集合的中位數沒有影響。

元素個數為奇數的情況稍有不同,但是也是類似的。

中位數的位置
回到這個題目上。

基於上述的原理,我們是否可以通過排除和中位數無關的元素,最終得到中位數?

此解法的重點是:要知道中位數的位置。(這不是扯淡嗎,我知道中位數的位置直接返回不就完了。- -!)

我們這里不是求中位數的確切位置,而是求中位數的大概位置。

假設題目給出的兩個有序集合為B1和B2,他們的長度為m和n。
設 midM = (m-1)/2(暫不考慮m=0的邊界情況)
設 midN = (n-1)/2
如果n%2=0:
設midNP = midN + 1
否則midNP = midN

那么這兩個集合的並集的中位數的值median,必定是:2<=median<=7。否則median無法將B1和B2的並集切分為等長的兩部分。(證明過程就不給出了,數學比較差,但是代碼運行結果證明這個推論是正確的。)

所以,根據上述推論可以得出,1,8是和中位數無關的元素。

消除元素1和8從而得到更短的兩個集合,如下圖。其並集中位數不變,還是4.5。

接下來重復以上的步驟。由於每次排除元素的數量是較短集合的一半元素,所以經過log(min(m,n))次迭代之后,到達邊界條件。

過程如動圖:

該算法的時間復雜度為O(log(min(m,n)))。

邊界條件處理:

最終到達邊界的情況下,nums1的長度可能是1或者2。對於這兩種情況,我們只需拿nums1剩下的數和nums2中間幾位數進行排序,就能得到中位數。
由於進行排序的數組長度最長是6,故其時間復雜度為O(1)。

class Solution(object):
    def findMedianSortedArrays(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: float
        """
        m = len(nums1)
        n = len(nums2)
        if n < m:
            temp = nums1
            nums1 = nums2
            nums2 = temp
            m = len(nums1)
            n = len(nums2)

        mid_m = (m - 1) / 2
        mid_n = (n - 1) / 2

        if m == 0:
            return nums2[mid_n] if (n % 2) == 1 else (nums2[mid_n] + nums2[mid_n + 1]) / 2.0

        if m == 1 or m == 2:
            if n < 3:
                nums1.extend(nums2)
            elif n % 2 == 1:
                nums1.extend(nums2[mid_n - 1:mid_n + 2])
            else:
                nums1.extend(nums2[mid_n - 1:mid_n + 3])
            nums1.sort()
            m = len(nums1)
            mid_m = (m - 1) / 2
            return nums1[mid_m] if m % 2 == 1 else (nums1[mid_m] + nums1[mid_m + 1]) / 2.0

        mid_np = mid_n if n % 2 == 1 else mid_n + 1

        if nums1[mid_m] == nums2[mid_np]:
            return nums1[mid_m]
        if nums1[mid_m] < nums2[mid_np]:
            return self.findMedianSortedArrays(nums1[mid_m:],nums2[:n-mid_m])
        return self.findMedianSortedArrays(nums1[:m-mid_m], nums2[mid_m:])

下面是依據官方題解簡化后的代碼,比較簡潔,但是理解的話需要參考下官方的思路

https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-s-114/

class Solution(object):
    def findMedianSortedArrays(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: float
        """
        def getkth(k, l1, l2):  
            if l1 == m: return nums2[l2 + k - 1]
            if l2 == n: return nums1[l1 + k - 1]
            if k == 1: return min(nums1[l1], nums2[l2])
            # 每次比較從開始起第 k/2-1 個數,並去掉較小的數組的前面部分 
            new_l1, new_l2 = min(l1 + k / 2, m), min(l2 + k / 2, n)
            if nums1[new_l1 - 1] < nums2[new_l2 - 1]:
                return getkth(k - new_l1 + l1, new_l1, l2)
            else:
                return getkth(k - new_l2 + l2, l1, new_l2)
        
        m, n, s = len(nums1), len(nums2), len(nums1) + len(nums2)
        if s % 2: 
            return getkth((s + 1) / 2, 0, 0) 
        return (getkth(s / 2, 0, 0) + getkth(s / 2 + 1, 0, 0)) / 2.0


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM