【算法-LeetCode】4. 寻找两个正序数组的中位数(数组;排序;中位数)

4. 寻找两个正序数组的中位数 - 力扣(LeetCode)

文章更新:2021年10月11日14:45:41

问题描述及示例

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

示例 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

示例 4:
输入:nums1 = [], nums2 = [1]
输出:1.00000

示例 5:
输入:nums1 = [2], nums2 = []
输出:2.00000

提示:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

我的题解

我的题解1(暴力解法;Array.sort())

直接思路就是先把两个数组合并在一起,然后对合并后的数组进行排序,再根据中位数的定义来返回符合题意的中位数。

当然除了这种偏暴力的解法外,合并两个有序数组的操作可以参考此前做过的一道合并两个有序数组的题目:

参考:【算法-LeetCode】88. 合并两个有序数组_赖念安的博客-CSDN博客

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number}
 */
var findMedianSortedArrays = function(nums1, nums2) {
  // 合并两个数组并对合并后的数组进行升序排序
  nums1 = [...nums1, ...nums2].sort((a,b) => a - b);
  // 获取合并数组的中间位置
  let half = nums1.length / 2;
  // 根据中位数的定义返回相应的值
  return Number.isInteger(half) ? (nums1[half] + nums1[half-1]) / 2 : nums1[Math.floor(half)];
};


提交记录
2094 / 2094 个通过测试用例
状态:通过
执行用时:156 ms, 在所有 JavaScript 提交中击败了13.68%的用户
内存消耗:43.9 MB, 在所有 JavaScript 提交中击败了26.90%的用户
时间:2021/10/11 14:48	

这种解法就没有利用到 nums1nums2 是有序的这一条件,性能表现自然不是很出色。

我的题解2(虚拟栈)

其实和上面提到的【LeetCode-88. 合并两个有序数组】是差不多的思路,只不过我这里没有真正地用到栈结构来存储排序后的元素,因为我们只需要获取中位数,所以没必要关心其他冗余的元素,自然就不必用一个栈结构来存储排序元素了,但是在理解时就可以想象有一个栈来存储了排序后元素,且下面的计数变量 count 其实就是该栈的栈长。

总体思路还是对 nums1nums2 中的元素进行排序,只不过这次我利用了这两个数组原本就是有序的这一条件。

而且不必要对 nums1nums2 中的所有元素都进行排序操作,只需要在虚拟栈的长度增长到两个数组合并后的一半长度时即可(也就是下面的 count 增加到了 half)。

详解请看下方注释:

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number}
 */
var findMedianSortedArrays = function(nums1, nums2) {
  // half用来标识中位数的位置,注意half可能是小数
  let half = (nums1.length + nums2.length) / 2;
  // count用于记录虚拟栈里有多少个元素
  let count = 0;
  // next用于存储下一个要放入虚拟栈中的元素
  let next = 0;
  // 开始对 nums1 和 nums2 中的元素合并到虚拟栈中并且保持有序状态,
  // 注意终止条件是虚拟栈的长度到达了中位数的位置
  while(count < Math.floor(half)) {
    // 用getNext函数获取下一个将要被压入虚拟栈中的元素
    next = getNext(nums1, nums2);
    // 想象将上面的next元素压入虚拟栈后,此时虚拟栈的长度应当加1
    count++;
  }
  // 因为要考虑合并数组长度的奇偶性,所以要考虑当前是否已经获取到了计算中位数的数据
  // 对于奇长度的合并数组,上面的循环终止条件只能保证next中存储了中位数前一位的元素
  // 对于偶长度的合并数组,上面的循环终止条件只能保证next中存储了计算中位数的其中一位的元素
  // 所以还需要再获取一次下一个将要被压入虚拟栈的元素 temp
  let temp = getNext(nums1, nums2);
  // 如果合并数组长度为奇,则直接返回temp即可,如果为偶,则需要计算中间两位的平均值
  return Number.isInteger(half) ? (temp + next) / 2 : temp;

  // getNext用于从 nArr1 和 nArr2 两个有序数组中获取下一个将要被压入虚拟栈的元素
  function getNext(nArr1, nArr2) {
    let next = 0;
    // 如果两个数组的长度都不为0
    if(nArr1.length && nArr2.length) {
      // 则选取头部元素较小的那个数组,将其头部元素弹出并赋值给next作为返回值返回
      next = nArr1[0] < nArr2[0] ? nArr1.shift() : nArr2.shift();
    } else {
      // 如果两个数组中有任何一个的长度为0,则选取还有元素的那个数组的头部元素作为返回值
      // 注意,这里绝不可能出现两个数组都为0的情况,因为上面的while循环只运行至找到中位数
      next = nArr1.length ? nArr1.shift() : nArr2.shift();
    }
    // 返回将要压入虚拟栈的元素
    return next;
  }
};


提交记录
2094 / 2094 个通过测试用例
状态:通过
执行用时:132 ms, 在所有 JavaScript 提交中击败了28.82%的用户
内存消耗:42.2 MB, 在所有 JavaScript 提交中击败了82.05%的用户
时间:2021/10/11 16:11

上面程序的关键在于【 getNext() 函数】和【判断中位数到底是怎么来的】这两点。因为不用完成所有元素的合并,所以性能表现肯定是比之前的程序要好一点的。

官方题解

更新:2021年7月29日18:43:21

因为我考虑到著作权归属问题,所以【官方题解】部分我不再粘贴具体的代码了,可到下方的链接中查看。

更新:2021年10月11日14:49:40

参考:寻找两个有序数组的中位数 - 寻找两个正序数组的中位数 - 力扣(LeetCode)

【更新结束】

有关参考

更新:2021年10月11日14:52:04
参考:展开语法 - JavaScript | MDN
参考:解构赋值 - JavaScript | MDN
参考:中位数_百度百科
更新:2021年10月11日15:19:25
参考:【算法-LeetCode】88. 合并两个有序数组_赖念安的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值