题目描述
给定两个大小分别为 m 和 n 的正序(非递减)整数数组 nums1 和 nums2,找出这两个数组的中位数。要求算法的时间复杂度为 O(log(m+n))O(log(m+n))。
解题思路
核心思想
通过二分查找将问题转化为在两个有序数组中寻找第 k 小的数,其中 k 为中位数的位置。
关键步骤
- 统一处理奇偶情况:
- 若
m + n为奇数,中位数是第(m+n+1)/2小的数。 - 若
m + n为偶数,中位数是第(m+n)/2和第(m+n)/2 + 1小的数的平均值。
- 若
- 二分查找策略:
- 比较
nums1和nums2的前k/2个元素。 - 较小部分的数组前
k/2个元素不包含中位数,可排除。
- 比较
- 边界条件处理:
- 当某个数组为空时,直接取另一个数组的第
k个元素。 - 当
k == 1时,返回两个数组首元素的较小值。
- 当某个数组为空时,直接取另一个数组的第
复杂度分析
- 时间复杂度:O(log(m+n))O(log(m+n)),每次递归或迭代将
k减半。 - 空间复杂度:O(1)O(1),仅需常数空间。
代码实现
- JavaScript
var findMedianSortedArrays = function(nums1, nums2) { const m = nums1.length, n = nums2.length; const total = m + n; const findKth = (k) => { let i = 0, j = 0; while (true) { if (i === m) return nums2[j + k - 1]; if (j === n) return nums1[i + k - 1]; if (k === 1) return Math.min(nums1[i], nums2[j]); const new_i = Math.min(i + Math.floor(k / 2) - 1, m - 1); const new_j = Math.min(j + Math.floor(k / 2) - 1, n - 1); if (nums1[new_i] <= nums2[new_j]) { k -= new_i - i + 1; i = new_i + 1; } else { k -= new_j - j + 1; j = new_j + 1; } } }; if (total % 2 === 1) { return findKth(Math.floor(total / 2) + 1); } else { return (findKth(total / 2) + findKth(total / 2 + 1)) / 2; } }; - python
def findMedianSortedArrays(nums1: list[int], nums2: list[int]) -> float: def find_kth(k): i, j = 0, 0 while True: # 边界条件 if i == m: return nums2[j + k - 1] if j == n: return nums1[i + k - 1] if k == 1: return min(nums1[i], nums2[j]) # 正常情况 new_i = min(i + k // 2 - 1, m - 1) new_j = min(j + k // 2 - 1, n - 1) if nums1[new_i] <= nums2[new_j]: k -= new_i - i + 1 i = new_i + 1 else: k -= new_j - j + 1 j = new_j + 1 m, n = len(nums1), len(nums2) total = m + n if total % 2 == 1: return find_kth((total + 1) // 2) else: return (find_kth(total // 2) + find_kth(total // 2 + 1)) / 2 - rust
impl Solution { pub fn find_median_sorted_arrays(nums1: Vec<i32>, nums2: Vec<i32>) -> f64 { let m = nums1.len(); let n = nums2.len(); fn find_kth(nums1: &[i32], nums2: &[i32], k: usize) -> i32 { let m = nums1.len(); let n = nums2.len(); let mut i = 0; let mut j = 0; let mut k = k; loop { if i == m { return nums2[j + k - 1]; } if j == n { return nums1[i + k - 1]; } if k == 1 { return nums1[i].min(nums2[j]); } let new_i = (i + k / 2 - 1).min(m - 1); let new_j = (j + k / 2 - 1).min(n - 1); if nums1[new_i] <= nums2[new_j] { k -= new_i - i + 1; i = new_i + 1; } else { k -= new_j - j + 1; j = new_j + 1; } } } let total = m + n; if total % 2 == 1 { find_kth(&nums1, &nums2, total / 2 + 1) as f64 } else { let left = find_kth(&nums1, &nums2, total / 2); let right = find_kth(&nums1, &nums2, total / 2 + 1); (left + right) as f64 / 2.0 } } }
实际应用场景
- 数据库查询优化:合并两个有序结果集时快速计算中位数。
- 实时数据流处理:动态统计多个有序数据源的中位数(需结合其他数据结构)。
- 财务分析:计算两支股票价格序列的中位数波动率。
- 生物信息学:比对两组基因测序数据的中间值差异。
相似题目
- 合并两个有序数组 解题思路:双指针合并,但时间复杂度为 O(m+n)O(m+n),不满足本题要求。
- 数据流的中位数 解题思路:使用堆动态维护中位数,适用于数据流场景。
- 第 k 个最大的元素 解题思路:快速选择算法,但针对无序数组。