寻找两个正序数组的中位数

📌 定义

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2,请找出这两个正序数组的中位数。

示例1:
nums1 = [1, 3]
nums2 = [2]
合并后: [1, 2, 3]
中位数: 2

示例2:
nums1 = [1, 2]
nums2 = [3, 4]
合并后: [1, 2, 3, 4]
中位数: (2 + 3) / 2 = 2.5

核心思路

使用二分查找的分治思想,在两个有序数组中找到合适的分割线,使得:

  1. 左边元素个数 = 右边元素个数(或差1)
  2. 左边最大值 ≤ 右边最小值
nums1: [1, 3, 5, 7]
nums2: [2, 4, 6, 8]

分割:
nums1_left: [1, 3] | nums1_right: [5, 7]
nums2_left: [2, 4] | nums2_right: [6, 8]

左边最大: max(3, 4) = 4
右边最小: min(5, 6) = 5

中位数 = (4 + 5) / 2 = 4.5

复杂度分析

方法时间复杂度空间复杂度说明
合并后排序O(m+n)O(m+n)简单但不满足题目要求
二分查找O(log(min(m,n)))O(1)最优解

Go 代码

Go 实现

package main
 
import (
    "fmt"
    "math"
)
 
func findMedianSortedArrays(nums1, nums2 []int) float64 {
    // 确保nums1是较短的数组
    if len(nums1) > len(nums2) {
        nums1, nums2 = nums2, nums1
    }
 
    m, n := len(nums1), len(nums2)
    left, right := 0, m
 
    for left <= right {
        partition1 := (left + right) / 2
        partition2 := (m + n + 1) / 2 - partition1
 
        maxLeft1 := math.MinInt32
        if partition1 > 0 {
            maxLeft1 = nums1[partition1-1]
        }
 
        maxLeft2 := math.MinInt32
        if partition2 > 0 {
            maxLeft2 = nums2[partition2-1]
        }
 
        minRight1 := math.MaxInt32
        if partition1 < m {
            minRight1 = nums1[partition1]
        }
 
        minRight2 := math.MaxInt32
        if partition2 < n {
            minRight2 = nums2[partition2]
        }
 
        if maxLeft1 <= minRight2 && maxLeft2 <= minRight1 {
            if (m+n)%2 == 1 {
                return float64(max(maxLeft1, maxLeft2))
            }
            return float64(max(maxLeft1, maxLeft2)+min(minRight1, minRight2)) / 2.0
        } else if maxLeft1 > minRight2 {
            right = partition1 - 1
        } else {
            left = partition1 + 1
        }
    }
 
    panic("输入数组不合法")
}
 
func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}
 
func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}
 
func main() {
    nums1 := []int{1, 3}
    nums2 := []int{2}
    fmt.Printf("中位数: %.1f\n", findMedianSortedArrays(nums1, nums2))
}

思路展开

分割原理

nums1: [1, 3, 5, 7]  (m = 4)
nums2: [2, 4, 6, 8, 10, 12]  (n = 6)

总长度 = 10,中位数左边应有 5 个元素

二分查找partition1:
partition1 = 2, partition2 = 5 - 2 = 3

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

检查条件:
max_left1 = 3, min_right2 = 8 → 3 <= 8 ✓
max_left2 = 6, min_right1 = 5 → 6 <= 5 ✗

partition1太小,需要右移

边界情况处理

  1. partition在边界:

    • partition1 = 0:max_left1 = -∞
    • partition1 = m:min_right1 = +∞
    • partition2 同理
  2. 奇偶数长度:

    • 奇数:返回左边最大值
    • 偶数:返回左边最大值和右边最小值的平均

经典题目

LeetCode 问题

相关问题

  • 寻找第K小的元素
  • 多个有序数组的中位数
  • 数据流中的中位数

⚖️ 优缺点

二分查找法

优点:

  • ✅ 时间最优:O(log(min(m,n)))
  • ✅ 空间最优:O(1)
  • ✅ 满足题目要求:对数时间复杂度

缺点:

  • ❌ 实现复杂:边界条件多
  • ❌ 理解困难:需要深刻理解二分思想

归并法

优点:

  • ✅ 简单直观:易于理解和实现

缺点:

  • ❌ 时间复杂度:O(m+n)
  • ❌ 空间复杂度:O(m+n)

🎨 应用场景

  1. 大数据处理:合并两个有序数据集
  2. 分布式系统:跨节点数据聚合
  3. 统计分析:快速计算中位数
  4. 数据库查询:排序合并连接

💡 扩展问题

相关主题


返回:分治算法 | 算法学习导航