LeeCode:Day2-两个有序数组合并成一个有序数组

437 阅读2分钟

思路:因为时间复杂是是logN,只有使用二分法才能满足要求。

假设两个数组分别是a和b,长度是m和n,我们要找的是m+n中的第k个数。

对a和b折半(先不考虑奇偶问题),分成a:0~i-1 | i~m-1,b:0~j-1 | j~n-1

①i+j=m+n-i-j👉j=(m+n)/2-i👉m<n才能保证j>0(因为i最大可以是m)

②**a[i-1]<b[j]b[j-1]<a[i]**至少有一个会满足条件(不可能两个都不满足)因为如果b[j]<a[i-1], 又已知a[i-1]<a[i]和b[j-1]<b[j],可以得出b[j-1]<a[i]

③当a[i-1]>b[j]时,说明a数组取得两个值取大了,b数组取得两个值取小了👉a数组中的取的数左移(b数组中的取的数右移)
当b[j-1]>a[i]时,说明b数组取得两个值取大了,a数组取得两个值取小了👉b数组中的取的数左移(a数组中的取的数右移)

④a数组的左移右移都是指选择左半边的中位数/选择右半边的中位数(比如说a[i-1]>b[j],a数组取得两个值取大了,a数组的右半边一定都在最后中位数的右边,在a数组的左半边继续寻找中位数)

⑤加上奇偶来考虑,如果

  1. m+n是偶数,左半边的数=右半边的数:i+j=m+n-i-j,j=(m+n)/2-i,最后的结果是左半边的最大者和右半边的最小者平均

  2. m+n是偶数,左半边的数比右半边的数多一个:i+j=m+n-i-j+1,j=(m+n+1)/2-i,最后的结果是左半边的最大者

  3. 但如果把j表示成向下取整,可以统一j的表达式j=Math.floor((m+n+1)/2-i)

function findTwoSortedArrayMidian(a, b) {
  // 保证m<n
  if (a.length > b.length) {
    let temp
    temp = a
    a = b
    b = temp
  }
  let iMin = 0,
    iMax = a.length,
    m = a.length,
    n = b.length,
    halfLen = Math.floor((a.length + b.length + 1) / 2)
  while (iMin < iMax) {
    let i = Math.floor((iMin + iMax) / 2)
    let j = halfLen - i
    // a数组取值小了,右移(取右半边)
    if (i < iMax && b[j - 1] > a[i]) {
      iMin = i + 1
    // a取值大了,左移(取左半边)
    } else if (i > iMin && a[i - 1] > b[j]) {
      iMax = i - 1
    // 分割的点符合要求
    } else {
      let maxLeft = 0
      // a数组全都在中位数右边
      if (i === 0) {
        maxLeft = b[j - 1]
      // a数组全部在中位数左边
      } else if (j == 0) {
        maxLeft = a[i - 1]
      // 一般情况
      } else {
        maxLeft = a[i - 1] > b[j - 1] ? a[i - 1] : b[j - 1]
      }
      // 奇数取左边最大值就可以
      if ((m + n) % 2 === 1) {
        return maxLeft
      }
      // 偶数取左边最大和右边最小的平均数
      let minRight = 0
      if (i == m) {
        minRight = b[j]
      } else if (j == n) {
        minRight = a[i]
      } else {
        minRight = a[i] < b[j] ? a[i] : b[j]
      }
      return (maxLeft + minRight) / 2
    }
  }
  return 0
}
let a = [1, 2, 3, 4],
  b = [7, 10]
let mid = findTwoSortedArrayMidian(a, b)
console.log(mid)