在问 · 算法题解

4. 两个正序数组的中位数

困难二分查找二分划分较短数组力扣原题 ↗

题目描述

输入两个已按从小到大排好序的数组 nums1 和 nums2,长度分别为 m 和 n。

把两个数组的全部元素放在一起看,返回它们的中位数。元素总数为奇数时,中位数是排序后正中间那个数;总数为偶数时,中位数是正中间两个数的平均值。

要求算法的时间复杂度为 O(log (m+n))。

示例 1
输入:nums1 = [1,3], nums2 = [2]
输出:2.0

解释:合并后为 [1,2,3],正中间是 2。

示例 2
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.5

解释:合并后为 [1,2,3,4],中间两个数是 2 和 3,平均值为 2.5。

数据范围

  • nums1.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -10^6 <= nums1[i], nums2[i] <= 10^6

解题思路:二分划分较短数组

在较短数组上二分切分。

为什么正确

切分两侧交叉边界有序。

复杂度分析

时间 O(log min(m,n)),额外空间 O(1)。

易错点

注意边界条件并在回溯或弹出后恢复状态。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
 def findMedianSortedArrays(self,A,B):
  if len(A)>len(B):A,B=B,A
  m,n=len(A),len(B);l,h=0,m
  while l<=h:
   i=(l+h)//2;j=(m+n+1)//2-i;al=A[i-1] if i else float('-inf');ar=A[i] if i<m else float('inf');bl=B[j-1] if j else float('-inf');br=B[j] if j<n else float('inf')
   if al<=br and bl<=ar:return float(max(al,bl)) if (m+n)%2 else (max(al,bl)+min(ar,br))/2
   if al>br:h=i-1
   else:l=i+1

载入编辑器,自己跑一遍 →

其他解法

归并法 O(m+n) 最直白;第 k 小二分每轮排除一半候选,达到 O(log(m+n));主解法只在较短数组上二分切分位置,达到 O(log min(m,n))。

解法 2:二分找第 k 小 时间 O(log(m+n)),空间 O(1)

把中位数转化为求两个数组合并后的第 k 小元素。每轮比较 A 和 B 各自第 k/2 个候选元素,较小的一方连同它之前的元素都不可能是第 k 小,整段排除并减小 k。某个数组取完或 k = 1 时直接得到答案。

思路比划分更容易想到,边界也较少。总长度为偶数时要求两次第 k 小,复杂度是 O(log(m+n)),略逊于主解法的 O(log min(m,n))。

class Solution:
    def findMedianSortedArrays(self, A, B):
        m, n = len(A), len(B)

        def kth(k):
            i = j = 0
            while True:
                if i == m:
                    return B[j + k - 1]
                if j == n:
                    return A[i + k - 1]
                if k == 1:
                    return min(A[i], B[j])
                half = k // 2
                ni = min(i + half, m) - 1
                nj = min(j + half, n) - 1
                if A[ni] <= B[nj]:
                    k -= ni - i + 1
                    i = ni + 1
                else:
                    k -= nj - j + 1
                    j = nj + 1

        total = m + n
        if total % 2:
            return float(kth(total // 2 + 1))
        return (kth(total // 2) + kth(total // 2 + 1)) / 2

解法 3:双指针归并到中点 时间 O(m+n),空间 O(1)

用两个指针按归并顺序从小到大取元素,只走到第 (m+n)/2 个位置。过程中保存最后两个取出的值 prev 和 cur。总长度为奇数时答案是 cur,为偶数时是 (prev + cur) / 2。

实现最简单,不容易出错。但时间是线性的,不满足题目对 O(log(m+n)) 的要求,适合作为对拍基准。

class Solution:
    def findMedianSortedArrays(self, A, B):
        m, n = len(A), len(B)
        total = m + n
        i = j = 0
        prev = cur = 0
        for _ in range(total // 2 + 1):
            prev = cur
            if i < m and (j >= n or A[i] <= B[j]):
                cur = A[i]
                i += 1
            else:
                cur = B[j]
                j += 1
        if total % 2:
            return float(cur)
        return (prev + cur) / 2

二分查找 专题的其他题目

查看 二分查找 全部题目 →