LeetCode 4. 寻找两个正序数组的中位数 | 算法-从菜鸟开始

简介: 本文是《算法-从菜鸟开始》系列文章的第5篇,欢迎收藏、留言、点赞。话不多说,让我们继续我们的算法之旅。

一、LeetCode 4. 寻找两个正序数组的中位数


题目介绍:


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


算法的时间复杂度应该为 O(log (m+n)) 。


示例:


输入: nums1 = [1,3], nums2 = [2]
输出: 2.00000
解释: 合并数组 = [1,2,3] ,中位数 2


解题思路


从题目上来分析,有两个要求要满足:


  1. 要查找出两个数组nums1nums2的中位数


  1. 要满足算法的时间复杂度是 O(log(m+n))O(log(m+n))O(log(m+n))

我们逐一来看。


数组的中位数:即两个数组合并后,取数组最中间的一个(数组长度为奇数)或两个数(数组长度为偶数)的平均数。 并且已知数组nums1nums2是正序(即由大到小)排列的。


那我们需要把两个数组进行从小到大的完全合并、排序,然后再查找、计算这个中位数吗?


其实并不需要,因为都是两个数组从小到大排列的,所以我们仅查找出符合要求的最中间一个或两个数。


我们可以设置一个数组nums,将所给数组nums1nums2中的值按小到达放入,直到符合的长度。


这个数组nums所需的长度计算规则:


// 根据数组的长度以及是偶数还是奇数,我们可以计算出需要的 - 基数取最中间的一位,偶数取最中间的两位
const maxNumsLength = len % 2 === 0 ? len / 2 + 1 : Math.ceil(len / 2);
// 加入输入的是 [1, 3] [2],通过计算可得maxNumsLength值为2
// 则nums中需要存入 [1, 2]


我们来看下代码的实现:


/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number}
 */
var findMedianSortedArrays = function(nums1, nums2) {
  // 获取两个数组的长度和
  const len = nums1.length + nums2.length;
  // 根据数组的长度以及是偶数还是奇数,我们可以计算出需要的 - 基数取一位,偶数取两位
  const maxNumsLength = len % 2 === 0 ? len / 2 + 1 : Math.ceil(len / 2);
  // 设置nums存储元素
  const nums = [];
  // 每次取出各自数组中的最小值(即数组的首位子元素),进行比较,将元素放入到nums中,并且将原数组中的该元素移除。
  // 每次存储nums1的首位
  let nums1First = nums1.shift();
  // 每次存储nums2的首位
  let nums2First = nums2.shift();
  // 该循环的条件为 nums.length < maxNumsLenth 并且nums1First 或者nums2First有值存在
  while ( (nums1First !== undefined || nums2First !== undefined) && nums.length < maxNumsLength) {
    // 边界校验:当nums1First为undefined时,表示此时的nums1数组已经空了,只存储nums2的元素即可
    if (nums1First === undefined) {
        nums.unshift(nums2First);
        nums2First = nums2.shift();
        continue;
    }
    // 边界校验:当nums2First为undefined时,表示此时的nums2数组已经空了,只存储nums1的元素即可
    if (nums2First === undefined) {
        nums.unshift(nums1First);
        nums1First = nums1.shift();
        continue;
    }
    // 不为空时,比较二者大小,取最小值,放入数组nums中
    if (nums1First > nums2First) {
      nums.unshift(nums2First);
      nums2First = nums2.shift();
    } else {
      nums.unshift(nums1First);
      nums1First = nums1.shift();
    }
  }
  // 中位数
  let median;
  // 如果是偶数取前两个否则取第一个
  if (len % 2 === 0) {
    median = (nums[1] + nums[0]) / 2;
  } else {
    median = nums[0];
  }
  // 保留5位小数
  return middle.toFixed(5)
};


提交代码,我们看下执行情况,从执行用时来看,这个算法实现的还不错。


网络异常,图片无法展示
|


但是,这样的算法实现达到了题目要求的O(log(m+n))O(log(m+n))O(log(m+n))了吗?

其实并没有,看下现在的时间复杂度是 O((m+n)2+1)O(\tfrac{(m+n)}{2} + 1)O(2(m+n)+1),即O(m+n)O(m+n)O(m+n)


二、二分法查找


二分法查找,是指在一组有序数组查找某个指定元素的算法。


每次查找的开始都是数组的二分之一处开始,查看是否符合要求。如果不符合,则向上或者向下进行查找。


举例说明:在数组 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]中查找数字10的索引。

首先使用常规算法:枚举比对


const findKey = (nums, target) => {
  // 遍历nums的所有元素
  for (let i = 0; i < nums.length; i++) {
    if (nums[i] === target){
      return i;
    }
  }
}
// 调用函数
const index = findKey([1, 2, 3, 5, 6], 6);
// 一共执行了6次比对
console.log(index); // 6


此种算法的时间复杂度是O(n)O(n)O(n)


如果使用二分法查找呢?


二分法的一个前提条件就是数组必需是有序的,可以是正序也可以是逆序。


const halfFindKey = (nums, target) {
  // 声明一个起始位置索引
  let startIndex = 0;
  let endIndex = nums.length - 1;
  let halfIndex;
  do {
    // 取二分之一处位置,考虑是偶数长度还是奇数长度
    // 取二分之一位置
    halfIndex = startIndex + Math.floor((endIndex - startIndex) / 2);
    // 当前值与target比对
    if (target === nums[halfIndex]) {
      return halfIndex
    } else {
      // 比较二者大小
      if (target > nums[halfIndex]) {
        // 说明target在右面
        startIndex = halfIndex + 1;
      } else {
        endIndex = halfIndex - 1;
      }
    }
    // 考虑边界情况,说明只有一个元素了
    if(startIndex === endIndex - 1) {
      // 判断是向上还是向下走
      return halfIndex > startIndex ? startIndex : endIndex;
    }
  } while (halfIndex)
}
// 调用函数
const index = halfFindKey([1, 2, 3, 4, 5, 6], 6);
// 执行3次循环,每次比对的元素分别是 nums[2]、nums[4]、nums[5]
console.log(index); // 3


该算法的时间复杂度是 O(logn)O(logn)O(logn),每次取数组的二分之一进行查询。


现在我们把目光再转移到这个 寻找两个正序数组的中位数问题上。



相关文章
|
1月前
|
存储 算法 JavaScript
怎么刷算法,leetcode上有哪些经典题目
怎么刷算法,leetcode上有哪些经典题目
16 0
|
6天前
|
算法
代码随想录算法训练营第六十天 | LeetCode 84. 柱状图中最大的矩形
代码随想录算法训练营第六十天 | LeetCode 84. 柱状图中最大的矩形
18 3
|
6天前
|
算法
代码随想录算法训练营第五十七天 | LeetCode 739. 每日温度、496. 下一个更大元素 I
代码随想录算法训练营第五十七天 | LeetCode 739. 每日温度、496. 下一个更大元素 I
11 3
|
6天前
|
算法
代码随想录算法训练营第五十六天 | LeetCode 647. 回文子串、516. 最长回文子序列、动态规划总结
代码随想录算法训练营第五十六天 | LeetCode 647. 回文子串、516. 最长回文子序列、动态规划总结
27 1
|
8天前
|
算法 DataX
二叉树(中)+Leetcode每日一题——“数据结构与算法”“剑指Offer55-I. 二叉树的深度”“100.相同的树”“965.单值二叉树”
二叉树(中)+Leetcode每日一题——“数据结构与算法”“剑指Offer55-I. 二叉树的深度”“100.相同的树”“965.单值二叉树”
|
9天前
|
C++
【力扣】2562. 找出数组的串联值
【力扣】2562. 找出数组的串联值
|
13天前
|
算法
算法系列--两个数组的dp问题(2)(下)
算法系列--两个数组的dp问题(2)(下)
18 0
|
13天前
|
存储 算法
算法系列--动态规划--⼦数组、⼦串系列(数组中连续的⼀段)(1)(下)
算法系列--动态规划--⼦数组、⼦串系列(数组中连续的⼀段)(1)
16 0
|
13天前
|
算法
算法系列--动态规划--⼦数组、⼦串系列(数组中连续的⼀段)(1)(上)
算法系列--动态规划--⼦数组、⼦串系列(数组中连续的⼀段)(1)
21 0
|
13天前
|
算法 计算机视觉
算法系列--两个数组的dp问题(1)(下)
算法系列--两个数组的dp问题(1)
18 0

热门文章

最新文章