[路飞]_寻找两个正序数组的中位数

197 阅读2分钟

「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战

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

示例3

输入: nums1 = [0,0], nums2 = [0,0]
输出: 0.00000

题解

如果没有这个时间复杂度要求为 O(log (m+n)),解决这个问题并不复杂。现在时间复杂度要求为 O(log (m+n))明显要使用二分法解决问题。

二分法

两个有序数组如何二分?

中位数:

  • 如果两数组长度之和(m+n)(m+n)为奇数,中位数是两个数组合并排序后的中间值
  • 如果两数组长度之和(m+n)(m+n)为偶数,中位数是两个数组合并排序后最中间两个数字的平均值

思路:

  • 假设对数组nums1和 nums2 nums1 和 nums2 分割
  • 中位数一定存在在这条分割线两侧,分割线左侧nums1和 nums2 nums1 和 nums2 较大值 和 分割线右侧 nums1和 nums2 nums1 和 nums2 较小值组成中位数
  • 分割线左边的所有元素需要满足的个数 Math.floor((n+m+1)/2)Math.floor( (n + m + 1) / 2)
    • 长度之和(m+n)(m+n)为偶数时,当在长度之和上+1时,整除向下取整,可以得到一个中位数,另一个中位数在计算结果上+1即可得到
    • 长度之和(m+n)(m+n)为奇数时,分割线的左边比右边多一个元素的要求,此时在长度之和上+1,会被2整除,可以直接得到中位数
  • 为了防止数组越界,要保证 nums1nums1的长度小于nums2nums2
  • 二分法边界 [0,m][0,m],这里 nums1nums1 的长度为假设为m
  • 假设 target=Math.floor((n+m+1)/2) target = Math.floor( (n + m + 1) / 2)
  • 如果 nums1nums1 给切分成[0,k],[k+1,m][0,k],[k+1,m] ,数组 numsnums 必须要切成 [0,targetk],[targetk+1,n][0,target - k],[target - k + 1 , n]
  • 这样两个数组就有了联系,在切分数组 nums1nums1 的时候 nums2nums2 根据规则切分即可
  • 终止条件,在[0,m][0,m]区间left==rightleft == right 找到了中间点

边界处理:

  • 分割线在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
  }
}