(python刷题)leetcode 第4题:寻找两个有序数组的中位数

题目在leetcode上的链接为:
https://leetcode-cn.com/problems/median-of-two-sorted-arrays/

题目描述在这里插入图片描述
解题思路
如果题目没有限制时间复杂度为 o(log(m+n))的话,那么就可以用归并排序的合并有序数组的方法解题,此时时间复杂度为 o(m+n)。要求时间复杂度o(log(m+n)),那么容易想到的就是使用二分法的思想解题。
首先我们要明确中位数的作用,中位数将一个数组划分为两个部分,这两个部分的特点是:

  1. 两个部分的长度相同
  2. 左边的元素都要小于等于右边的元素

回到题目,要求两个有序数组 A,B 的中位数,我们可以把 A 以索引 i 划分为两个部分,左边部分为 A[0] 到 A[i-1] ,右边部分为 A[i] 到 A[m-1](m为数组 A 的长度);把 B 以索引 j 划分为两个部分,左右部分为 B[0] 到 B[j-1],右边部分为 B[j] 到 B[n-1](n为数组 B 的长度);然后将 A,B 的左边进行组合为 left_part,将 A,B 的右边进行组合为 right_part。我们要找到中位数,需要寻找的 i,j 能满足以下两个条件:

  1. len(left_part)=len(right_part)
  2. max(left_part)<=min(right_part)

对于第1个条件,len(left_part)=i+j,为了同时考虑 m+n 可能为奇数或者偶数的情况,我们令 i+j=(m+n+1)/2,即 j=(m+n+1)/2-i
当 m+n 为奇数时,中位数为max(left_part);
当 m+n 为偶数时,中位数为 (max(left_part)+min(right_part))/2。

对于第2个条件,max(left_part)=max(A[i-1],B[j-1]),min(right_part)=min(A[i],B[j])
要使得max(left_part)<=min(right_part),我们只需要使得
A[i-1]<=B[j] 且 B[j-1]<=A[i]

所以我们只需要在[0,m]内寻找 i,使得

A[i-1]<=B[j] 且 B[j-1]<=A[i],其中j=(m+n+1)/2-i

ps.此时需要注意一点,由于 j>=0,而 i 在[0,m]中,所以必须使得 m<=n

要在[0,m]中查找满足条件的 i,我们可以使用二分查找法。
算法的具体步骤为:

  1. 初始化 iMin=0,iMax=m
  2. 在 iMin<=iMax的条件下循环查找 i,令i=(iMin+iMax)/2,j=(m+n+1)-i
  3. 如果 A[i-1]<=B[j] 且 B[j-1]<=A[i],那么查找到了 i,循环结束
    如果 A[i-1]>B[j],那么说明 A[i-1] 太大了,为了减小 A[i-1] ,我们需要减小 i,即令 iMax=i-1,此时 j 会增大,有可能会满足 A[i-1]<=B[j] 的条件,并返回步骤2
    如果 B[j-1]>A[i],说明 A[i] 太小了,为了增大 A[i],我们需要增大 i,即令iMin=i+1,此时 j 会减小,有可能会满足 B[j-1]<=A[i] 的条件,并返回步骤2
  4. 如果 m+n 为奇数,中位数为 max(left_part);
    如果 m+n 为偶数,中位数为 (max(left_part)+min(right_part))/2

但是在上述步骤中,我们并没有考虑到 A[i-1],A[i],B[j-1],B[j]不存在的情况,即当 i=0,i=m,j=0,j=n的边界条件。下面我们来分析边界条件的影响。
如果i=0,则 A[i-1] 不存在,此时 A[i-1]<=B[j] 这个式子也不存在。那么 i=0 意味着什么呢?i=0 意味着我们把整个数组 A 都放到了 right_part 中,此时 max(left_part)=B[j-1],min(right_part)=min(A[i],B[j]),要满足 max(left_part)<=max(right_part) 的条件,只需要 B[j-1]<=A[i] 即可,也就是说当 i=0 是其实不需要考虑 A[i-1]<=B[j] 这个条件。也就是说,此时 i=0 且 B[j-1]<=A[i] 成立的话就已经寻找到了满足条件的 i。
所以当考虑了边界条件后,上述算法只需要改动第3步骤即可。
改动后的第3步骤为:
如果 (i=0 或 j=n 或 A[i-1]<=B[j]) 且 (j=0 或 i=m 或 B[j-1]<=A[i]),那么查找到了 i,循环结束
如果 i>0 且 j<n 且 A[i-1]>B[j],那么说明 A[i-1] 太大了,为了减小 A[i-1] ,我们需要减小 i,即令 iMax=i-1,此时 j 会增大,有可能会满足 A[i-1]<=B[j] 的条件,并返回步骤2
如果 j>0 且 i<m 且 B[j-1]>A[i],说明 A[i] 太小了,为了增大 A[i],我们需要增大 i,即令iMin=i+1,此时 j 会减小,有可能会满足 B[j-1]<=A[i] 的条件,并返回步骤2

复杂度分析:
由于是在 [0,m] 中使用二分法查找 i,所以时间复杂度为 o(log(min(m,n)))
由于只需要创建常数个新的变量,所以空间复杂度为o(1)

python代码:

class Solution(object):
    def findMedianSortedArrays(self, A, B):
        """
        :type A: List[int]
        :type B: List[int]
        :rtype: float
        """ 
        m = len(A)
        n = len(B)
        # 保证 m <= n
        if m > n:
            temp = A 
            A = B
            B = temp
            t = m 
            m = n
            n = t
        iMin = 0
        iMax = m 
        halfLen = (m + n + 1) // 2
        while iMin <= iMax:
            i = (iMin + iMax) // 2
            j = halfLen - i 
            if i > 0 and j < n and A[i - 1] > B[j]:
                iMax = i -1
            elif i < m and j > 0 and B[j - 1] > A[i]:
                iMin = i + 1
            else:
            	# 此时已经寻找到了满足条件的i
                # 寻找maxLeft
                if i == 0:
                    maxLeft = B[j - 1]
                elif j == 0:
                    maxLeft = A[i - 1]
                else:
                    maxLeft = max(A[i -1], B[j - 1])

                # 一定要先判断是奇数要直接返回,因为可能存在 m+n 为1时,这时如果再计算minRigh 就会内存溢出
                if (m + n) % 2 == 1:
                    return maxLeft

                # 寻找minRight
                if i == m:
                    minRight = B[j]
                elif j == n:
                    minRight =  A[i]
                else:
                    minRight = min(A[i], B[j])

                return (maxLeft + minRight) / 2.0
       
发布了37 篇原创文章 · 获赞 6 · 访问量 5399

猜你喜欢

转载自blog.csdn.net/qq_37891889/article/details/104181099