题目描述
给定两个有序数组nums1和nums2,请找出这两个数组的中位数。
解题思路
暴力法
最简单直观的方法是先将两个数组合并成一个有序数组,然后直接根据合并后数组的长度是奇数还是偶数来找到中位数,这种方法的时间复杂度为O(m+n)。
二分查找法
为了满足时间复杂度的要求,可以采用二分查找的思想。主要思路是将问题转化为在两个有序数组中寻找第k小的数,其中k为(m+n)/2(当数组总长度为奇数时)或(m+n)/2和(m+n)/2+1(当数组总长度为偶数时,需要找两个数来求平均值)。
具体步骤如下:
- 初始化:设定两个指针left_1和left_2分别指向nums1和nums2的起始位置,以及k的初始值为(m+n)/2(或(m+n)/2+1,根据总长度是奇数还是偶数决定)。
- 比较和移动指针:
比较nums1[left_1 + k/2 - 1](注意下标不要越界)和nums2[left_2 + k/2 - 1]的值:- 如果前者较小,则说明nums1[left_1:left_1+k/2]中的元素都小于第k小的数,因此可以将这部分元素排除,将left_1增加k/2,k减去k/2(因为已经排除了这么多元素)。
- 同理,如果后者较小或两者相等,则排除nums2中的相应元素。
- 边界情况处理:
- 如果某个数组被完全排除(即left_1或left_2超出数组长度),则直接返回另一个数组中第k-1个位置的元素(因为此时已经确定了前k-1小的元素都在剩下的数组中)。
- 如果k变为1,则返回两个数组中当前left_1和left_2指向的元素中较小的一个。
- 递归或循环:重复上述步骤,直到找到第k小的数。
- 计算中位数:
- 如果数组总长度为奇数,则第k小的数就是中位数。
- 如果数组总长度为偶数,则需要找到第(m+n)/2和第(m+n)/2+1小的数,然后求它们的平均值。
代码示例
var findMedianSortedArrays = function(nums1, nums2) {
// 确保nums1是较短的数组
if (nums1.length > nums2.length) {
[nums1, nums2] = [nums2, nums1];
}
const m = nums1.length;
const n = nums2.length;
// 二分查找的左右边界
let left = 0, right = m;
let halfLen = Math.floor((m + n + 1) / 2);
while (left <= right) {
const i = Math.floor((left + right) / 2);
const j = halfLen - i;
const nums1LeftMax = (i === 0) ? -Infinity : nums1[i - 1];
const nums1RightMin = (i === m) ? Infinity : nums1[i];
const nums2LeftMax = (j === 0) ? -Infinity : nums2[j - 1];
const nums2RightMin = (j === n) ? Infinity : nums2[j];
if (nums1LeftMax <= nums2RightMin && nums2LeftMax <= nums1RightMin) {
// 当总长度是奇数时,直接返回两个右边界中的较小值
if ((m + n) % 2 === 1) {
return Math.max(nums1LeftMax, nums2LeftMax);
}
// 当总长度是偶数时,返回两个左右边界值的平均值
return (Math.max(nums1LeftMax, nums2LeftMax) + Math.min(nums1RightMin, nums2RightMin)) / 2;
} else if (nums1LeftMax > nums2RightMin) {
// nums1的分割点需要向左移动
right = i - 1;
} else {
// nums1的分割点需要向右移动
left = i + 1;
}
}
// 如果没有找到有效的分割(理论上不应该发生)
throw new Error('No valid median found');
};