[路飞]_leetcode-4-寻找两个正序数组的中位数

简介: leetcode-4-寻找两个正序数组的中位数

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


「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战


[题目地址][B站地址]


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


算法的时间复杂度应该为 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
复制代码


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


通过次数552,171提交次数1,347,470


本题需要知道的是如果输入两个数组的长度之和 len为奇数,则中位数为中间一个元素(下标为 len>>1 )的值,否则为中间两个元素 (下标为 len>>1len>>1+1) 的平均值)

本题解题并不难,难点在于题解的时间复杂度应该为 O(log (m+n))


首先我们看一下时间复杂度没达到给定要求但是比较好理解的题解 => 题解1 & 题解2


题解1


解题思路如下:


  1. 通过结构赋值将输入数组合并为一个数组
  2. 使用 sort 对合并数组进行排序
  3. 根据合并数组长度找到中位数返回


代码如下:


var findMedianSortedArrays = function(nums1, nums2) {
  // 合并数组
  const arr = [...nums1,...nums2],
  // 获取数组长度
  len = arr.length;
  // sort 排序
  arr.sort((a,b) => a-b)
  // 获取中间下标
  const midIndex = len>>1
  // 判断数组长度奇偶返回对应结果
  return len%2?arr[midIndex]:(arr[midIndex-1]+arr[midIndex])/2
}
复制代码


因为 Array.sort 函数各浏览器实现并不相同,这里以 v8 为例,使用快排加插入排序,所以本题解时间复杂度可以定为 O((m+n)log (m+n))


题解2


解题思路如下:


  1. 获取两数组长度之和 len
  2. 初始化一个数组 arr存放输入两数组排序后的结果
  3. 获取中位数下标 mid = len>>1 (如果 len 为奇数,则中位数为 arr[mid] 的值,否则为 (arr[mid-1]+arr[mid])/2))
  4. 初始化两个指针tail1tail2 分别指向两个数组的下标 0
  5. 每次将两指针指向数字中的最小值放入 arr,并将指针向后移动一位,直到 arr.length>mid
  6. 判断 len 奇偶返回中位数


代码如下:


var findMedianSortedArrays = function(nums1, nums2) {
  // 获取两数组长度
  const len1 = nums1.length,
  len2 = nums2.length,
  //  获取两数组中长度
  len = len1+len2,
  // 初始化arr存放有序元素
  arr = [],
  // 获取第一个中位数下标
  mid=len>>1;
  // 初始化指针
  let tail1 = tail2 = 0;
  // 当两数组不为空,将两指针更小值放入arr
  while(tail1<len1||tail2<len2){
    if(tail1===len1){
      arr.push(nums2[tail2]);
      tail2++;
    }else if(tail2===len2){
      arr.push(nums1[tail1]);
      tail1++;
    }else if(nums1[tail1]>nums2[tail2]){
      arr.push(nums2[tail2]);
      tail2++;
    }else{
      arr.push(nums1[tail1]);
      tail1++;
    }
    // 如果数组中有mid+1个元素的时候,即可返回结果
    if(arr.length>mid){
      if(len%2) return arr[mid]
      return (arr[mid-1]+arr[mid])/2
    }
  }
}
复制代码


该题解时间复杂度为 O((m+n)>>1+1)


题解3


通常在一个有序数组中查找某个值,又要求时间复杂度为 O(log (n)),就要用到 二分查找


此题一个复杂的点就是在两个有序数组中,找到第 k 大的值,我们一起来看一下该题如何利用 二分查找解题


解题思路如下:


  1. 编写一个函数在两个有序数组中获取第 k 大的值
  2. 如果两数组长度之和 len 为奇数,只需要找到第 (len+1)>>1 大的值即可,否则需要找到第 (len+1)>>1(len+1)>>1+1 大的元素,返回它们的平均数即可


该题解解题思路很简单,难点在于如何利用 二分查找 在两个有序数组中查到第 k 大的元素


思路如下:


  1. 函数传入两个数组及两个数组的起始下标以及 k
  2. 判断某个数组是否为空,即 ind===arr.length,如果条件成立,则结果值肯定在另一个数组中,返回另一个数组对应下标元素即可
  3. 如果 k===1,则说明只需要找到第一大的元素即可,此时返回两个下标位置元素的最小值即可
  4. 尝试将数组1下标向后调整 k>>1位,如果数组长度不够走 k>>1 步,则调整到数组末尾
  5. 根据数组1走的步数调整数组2的步数 k-step1,如果数组长度不够走 k-step1 步,则调整到数组末尾
  6. 因为存在数组2的长度不够数组1剩余步数的情况,所以求得数组2的步数后需要倒推一下数组1的步数
  7. 如果调整后数组1位置的值小于数组2位置的值,则说明目标值肯定不在 arr1[ind1+step1] 之前的元素中,因为它们是当前两数组区间中前step1小的元素的一部分,更新数组1的起始下标,反之更新数组2的起始下标


假设函数传入参数依次为 arr1,ind1,arr2,ind2,k


该题解第5步适用于 getNum([1],0,[3,4,5,6,7,8],0,4) 的情况


第6、7步适用于 getNum([3,4,5,6,7,8],0,[1],0,4) 的情况


代码如下:


var findMedianSortedArrays = function (nums1, nums2) {
  // 两数组长度之和
  const len = nums1.length+nums2.length,
  // 中位数为第 mid 大的值
  // 如果len为奇数,则中位数就是第 mid 大的值
  // 否则是第 mid 和第 mid+1 大的值
  mid = (len+1)>>1;
  // 奇数
  if(len%2) return getNum(nums1,0,nums2,0,mid)
  // 偶数 
  return (getNum(nums1,0,nums2,0,mid)+getNum(nums1,0,nums2,0,mid+1))/2
  /**
    @param  {array} arr1 被寻找的第一个数组
    @param  {number} ind1 第一个数组的起始下标 
    @param  {array} arr2 被寻找的第二个数组
    @param  {number} ind2 第二个数组的起始下标 
    @param  {number} k 要寻找的是第k大值
    @return {number} 找到的第k大值
   */
  function getNum(arr1,ind1,arr2,ind2,k){
    // 某个数组空了,剩下的个数取另一个没空的数组的值
    if(ind1 === arr1.length) return arr2[ind2+k-1]
    if(ind2 === arr2.length) return arr1[ind1+k-1]
    // 还差一步,返回当前两个值中的最小值
    if(k==1) return Math.min(arr1[ind1],arr2[ind2])
    // 第一个数组尝试向后走一半距离,如果不够,走到数组末尾
    let step1 = Math.min(k>>1,arr1.length-ind1)
    // 第二个数组尝试向后走k-第一个数组走的距离,如果不够,走到数组末尾
    let step2 = Math.min(k-step1,arr2.length-ind2)
    // 再用step2倒推一下step1
    step1 = k-step2
    // 说明 arr1[ind1+step1] 之前的元素可以排除
    if(arr1[ind1+step1-1]<arr2[ind2+step2-1]){
      return getNum(arr1,ind1+step1,arr2,ind2,k-step1)
    }
    // 反之 arr2[ind2+step2] 之前的元素可以排除
    return getNum(arr1,ind1,arr2,ind2+step2,k-step2)
  }
};
复制代码


其实因为本题的数据量并不大,所以以上三种方法多次提交都可以达到耗时击败 90+% 用户的程度,但是从时间复杂度来讲,只有最后一种达到了本题要求的 O(log (m+n))

至此我们就完成了 leetcode-4-寻找两个正序数组的中位数


如有任何问题或建议,欢迎留言讨论!

相关文章
|
5月前
|
SQL 算法 数据挖掘
LeetCode 第四题:寻找两个正序数组的中位数 【4/1000 】【python + go】
LeetCode 第四题:寻找两个正序数组的中位数 【4/1000 】【python + go】
|
6月前
|
算法
leetcode代码记录(寻找两个正序数组的中位数
leetcode代码记录(寻找两个正序数组的中位数
43 2
|
6月前
|
算法
【力扣】4. 寻找两个正序数组的中位数
【力扣】4. 寻找两个正序数组的中位数
|
6月前
|
存储 算法
《LeetCode 热题 HOT 100》——寻找两个正序数组的中位数
《LeetCode 热题 HOT 100》——寻找两个正序数组的中位数
|
6月前
|
存储 算法 Go
LeetCode第四题: 寻找两个正序数组的中位数
给定两个大小分别为m和n的正序(从小到大)数组nums1和nums2。请你找出并返回这两个正序数组的中位数。
|
2月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
54 6
|
3月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
106 2
|
7天前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
8 1
|
2月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口