题目描述
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log(m+n))O(log (m+n))O(log(m+n)) 。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
提示:
- nums1.length==mnums1.length == mnums1.length==m
- nums2.length==nnums2.length == nnums2.length==n
- 0<=m<=10000 <= m <= 10000<=m<=1000
- 0<=n<=10000 <= n <= 10000<=n<=1000
- 1<=m+n<=20001 <= m + n <= 20001<=m+n<=2000
- −106<=nums1[i],nums2[i]<=106-10^6 <= nums1[i], nums2[i] <= 10^6−106<=nums1[i],nums2[i]<=106
思考
采用二分查找法,通过在较短数组上划分左右两部分,找到两个数组的“分割线”,使得分割线左侧的所有元素均小于等于右侧的所有元素。利用中位数的定义(分割线左右元素数量相等或相差1),通过二分调整分割线位置,最终计算出中位数。该方法时间复杂度为O(log(min(m,n))),满足O(log(m+n))的要求。
算法核心原理
- 中位数的本质:对于合并后长度为
L的数组,中位数是第L//2个元素(奇数)或第L//2-1与L//2个元素的平均值(偶数)。 - 分割线特性:设分割线在
nums1的位置为i(左侧有i个元素),在nums2的位置为j(左侧有j个元素),需满足:i + j = (m + n + 1) // 2(左侧总元素比右侧多1,确保奇数时左侧最大值为中位数)nums1[i-1] <= nums2[j]且nums2[j-1] <= nums1[i](左侧所有元素≤右侧所有元素)
算法过程
- 确保短数组在前:交换
nums1和nums2,使m ≤ n(在短数组上二分更高效)。 - 二分查找分割线:
- 初始化
l=0,r=m(nums1的分割范围)。 - 计算
i = (l + r) // 2(nums1的分割位置),j = halfLen - i(nums2的分割位置,halfLen = (m + n + 1) // 2)。 - 定义边界值(处理分割线在数组首尾的情况):
maxL1:nums1左侧最大值(i=0时为-Infinity)minR1:nums1右侧最小值(i=m时为Infinity)maxL2:nums2左侧最大值(j=0时为-Infinity)minR2:nums2右侧最小值(j=n时为Infinity)
- 调整分割线:
- 若
maxL1 > minR2:分割线需左移(r = i - 1)。 - 若
maxL2 > minR1:分割线需右移(l = i + 1)。 - 否则:找到有效分割线,计算中位数。
- 若
- 初始化
- 计算中位数:
- 若总长度为奇数:中位数 =
max(maxL1, maxL2)。 - 若总长度为偶数:中位数 =
(max(maxL1, maxL2) + min(minR1, minR2)) / 2。
- 若总长度为奇数:中位数 =
时空复杂度分析
- 时间复杂度:O(log(min(m,n))),仅在较短数组上进行二分查找,迭代次数为log(min(m,n))。
- 空间复杂度:O(1),仅使用常数个变量存储边界值和指针,无额外空间开销。
代码
/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number}
*/
var findMedianSortedArrays = function(nums1, nums2) {
let [m, n] = [nums1.length, nums2.length];
if (m > n) {
[m, n] = [n, m];
[nums1, nums2] = [nums2, nums1];
}
const halfLen = Math.floor((m + n + 1) / 2);
let l = 0, r = m;
while (l <= r) {
let i = l + Math.floor((r-l)/2);
let j = halfLen - i;
let maxL1 = i === 0 ? -Infinity : nums1[i-1];
let minR1 = i === m ? Infinity : nums1[i];
let maxL2 = j === 0 ? -Infinity : nums2[j-1];
let minR2 = j === n ? Infinity : nums2[j];
if (maxL1 <= minR2 && maxL2 <= minR1) {
const len = m + n;
if (len % 2 === 0) {
return (Math.max(maxL1, maxL2) + Math.min(minR1, minR2))/2;
} else {
return Math.max(maxL1, maxL2);
}
} else if (maxL1 > minR2) {
r = i-1;
} else if (maxL2 > minR1) {
l = i + 1;
}
}
};
710

被折叠的 条评论
为什么被折叠?



