「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」
题目
给定两个大小分别为 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
示例3
输入: nums1 = [0,0], nums2 = [0,0]
输出: 0.00000
题解
如果没有这个时间复杂度要求为 O(log (m+n)),解决这个问题并不复杂。现在时间复杂度要求为 O(log (m+n))明显要使用二分法解决问题。
二分法
两个有序数组如何二分?
中位数:
- 如果两数组长度之和为奇数,中位数是两个数组合并排序后的中间值
- 如果两数组长度之和为偶数,中位数是两个数组合并排序后最中间两个数字的平均值
思路:
- 假设对数组 分割
- 中位数一定存在在这条分割线两侧,分割线左侧 较大值 和 分割线右侧 较小值组成中位数
- 分割线左边的所有元素需要满足的个数
- 长度之和为偶数时,当在长度之和上+1时,整除向下取整,可以得到一个中位数,另一个中位数在计算结果上+1即可得到
- 长度之和为奇数时,分割线的左边比右边多一个元素的要求,此时在长度之和上+1,会被2整除,可以直接得到中位数
- 为了防止数组越界,要保证 的长度小于
- 二分法边界 ,这里 的长度为假设为m
- 假设
- 如果 给切分成 ,数组 必须要切成
- 这样两个数组就有了联系,在切分数组 的时候 根据规则切分即可
- 终止条件,在区间 找到了中间点
边界处理:
- 分割线在nums1最左边时,分割线在nums1最右边时
- 分割线在nums2最左边时,分割线在nums2最右边时
- 数组之和为奇数
- 数组之和为偶数
- 返回结果
根据上述思路编辑代码如下:
代码
var findMedianSortedArrays = function (nums1, nums2) {
if (nums1.length > nums2.length) {
let t = nums1
nums1 = nums2
nums2 = t
}
const len1 = nums1.length
const len2 = nums2.length
const target = (len1 + len2 + 1) >> 1
let left = 0
let right = len1
while (left < right) {
const m = left + ((right - left + 1) >> 1)
const j = target - m
if (nums1[m - 1] > nums2[j]) {
right = m - 1
} else {
left = m
}
}
let m = left
let n = target - m
let n1Max = m === 0 ? -Infinity : nums1[m - 1]
let n1Min = m === len1 ? Infinity : nums1[m]
let n2Max = n === 0 ? -Infinity : nums2[n - 1]
let n2Min = n === len2 ? Infinity : nums2[n]
if ((len2 + len1) % 2 === 1) {
return Math.max(n1Max, n2Max)
} else {
const max = Math.max(n1Max, n2Max)
const min = Math.min(n1Min, n2Min)
return (max + min) / 2
}
}