原题链接:4.寻找两个正序数组的中位数
给定两个大小分别为 m 和 n 的 正序(从小到大) 数组nums1和nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 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 == m
- nums2.length == n
- 0 <= m <= 1000
- 0 <= n <= 1000
- 1 <= m + n <= 2000
- -10^6 <= nums1[i], nums2[i] <= 10^6
解题:
这个解法的精髓在于,找到nums1、nums2的分割线i, j,能满足:
nums1[i-1] <= nums2[j] && nums2[j-1] <= nums1[i](这里i-1就代表了分割线左边)
i,j对应各自数组的(左边最大值 + 右边最小值)/ 2,就是题目要求的中位数。
那么接下来整理一下解题步骤:
首先,对nums1符合条件的分割线位置i查询,使用二分查找,终止循环的条件为left==right。
low和high的更新逻辑是:
nums1[i-1] > nums2[j]时,说明当前分割线左边比nums2[j]大,所以需要找一个更小的数,右边界缩紧nums1[i-1] <= nums2[j]时,说明当前分割线左边值比nums2[j]小,所以可以找找看有没有更大符合条件的值,左边界缩紧
经过查询后,这个left就是nums1数组的 i分割位置
而nums2的 j 的分割位置呢也就是(nums1.length + nums2.length)/2 - i。
其次,不能忘记的是,还需要处理一些特殊情况:
-
如果
i === 0,代表i分割线在nums1数组的最左端,那
nums1LeftMax的值需要变成Numer.MIN_VALUE(最后求解max( nums1LeftMax, nums2LeftMax)时,计算出的最大值就是nums2LeftMax) -
如果
i===nums1.length,代表i分割线在nums1数组的最右端, 那nums1RightMin的值需要变成Numer.Max_Value(最后求解min(nums1RightMin , nums2RightMin)时,计算出的最小值就是nums2RightMin)
剩下对j处理时的逻辑同理
最后,找到了分割线之后,可以算出中位数就是:
// 如果总长度是偶数:
(
max( nums1LeftMax, nums2LeftMax ) + min(nums1RightMin , nums2RightMin)
) / 2
// 如果总长度是奇数:
max( nums1LeftMax, nums2LeftMax )
下面来实现一下:
var findMedianSortedArrays = function (nums1, nums2) {
// 首先保证 nums1是相对短的那个数组
if(nums1.length > nums2.length){
const tmp = nums1;
nums1 = nums2;
nums2 = tmp;
}
// 提前准备好基础数据
const len1 = nums1.length,
len2 = nums2.length,
totalLength = len1 + len2,
totalLeft = Math.ceil(totalLength / 2);
// 注意这里是ceil,如果是奇数长度数组,那么这个位置是中间靠右
let left =0, right = len1;
// 通过二分法,不断将left、right逼近符合 nums1[i-1] <= nums2[j] 的位置
while(left < right){
const i = Math.ceil((left + right) / 2);
const j = totalLeft - i;
if(nums1[i - 1] > nums2[j]) {
right = i - 1;
}else{
left = i;
}
}
// 至此,找到了分割nums1的i位置
const i = left;
const k = totalLeft - i;
// 处理边界情况
const n1LeftMax = i === 0 ? Number.MIN_VALUE : nums1[i - 1];
const n1RightMin = i === len1 ? Number.MAX_VALUE : nums1[i];
const n2LeftMax = j === 0 ? Number.MIN_VALUE : nums2[j - 1];
const n2RightMin = j === len2 ? Number.MAX_VALUE : nums2[j];
// 先判断长度是奇数还是偶数
if(totalLength % 2 === 0){
return (
Math.max(n1LeftMax, n2LeftMax) + Math.min(n1RightMin, n2RightMin)
) / 2;
} else {
return Math.max(n1LeftMax, n2LeftMax);
}
}