一道非常经典的题目,Median of Two Sorted Arrays。(PS:leetcode 我已经做了 190 道,欢迎围观全部题解 https://github.com/hanzichi/leetcode

题意非常简单,给定两个有序的数组,求中位数,难度系数给的是 Hard,希望的复杂度是 log 级别。回顾下中位数,对于一个有序数组,如果数组长度是奇数,那么中位数就是中间那个值,如果长度是偶数,就是中间两个数的平均数。

O(nlogn)

最容易想到的解法是 O(nlogn) 的解法,将两个数组合并成一个,然后排序,排序用 JavaScript 数组内置的 sort 函数,复杂度 nlogn,最后根据数组长度选择中位数,非常容易理解。

var findMedianSortedArrays = function(nums1, nums2) {
  // 合并数组
  var s = nums1.concat(nums2);

  // 排序
  s.sort(function(a, b) {
    return a - b;
  });

  var len = s.length;

  // 根据数组长度求中位数
  if (len & 1) return s[~~(len / 2)];
  else return (s[len / 2 - 1] + s[len / 2]) / 2;
};

O(n)

将两个有序的数组合并成一个有序的数组,想到了什么?没错,这正是归并排序的关键一步。

关于归并排序,请看楼主以前写的这篇 【前端也要学点算法】 归并排序的JavaScript实现。这正是写博客的好处之一,可以将知识体系串联起来,比如这里我就不用介绍归并排序了,因为那篇文章我已经写的非常非常清楚了,而且就算现在我忘了,稍微看一遍也就能记起来了。

我们把归并排序中的 merge 函数拉出来,就 ok 了,一次线性的循环,复杂度降到了 O(n)。(其实应该是 O(n+m),方案一也一样,就不多加区别了)

var findMedianSortedArrays = function(nums1, nums2) {
  // 合并数组,返回有序数组
  var s = merge(nums1, nums2);

  var len = s.length;

  // 根据数组长度求中位数
  if (len & 1) return s[~~(len / 2)];
  else return (s[len / 2 - 1] + s[len / 2]) / 2;
};

function merge(left, right) {
  var tmp = [];

  while (left.length && right.length) {
    if (left[0] < right[0])
      tmp.push(left.shift());
    else
      tmp.push(right.shift());
  }

  return tmp.concat(left, right);
}

PS:其实对于此题,排序到一半就 ok 了,绝对的复杂度可以降到一半,不过也没什么必要。

O(logn)

以上两种解法,个人觉得难度系数对应的分别是 Easy 和 Medium,而 Hard 的解法应该把复杂度降到 log 级别。

换个方式思考,给出两个有序数组,假设两个数组的长度和是 len,如果 len 为奇数,那么我们求的就是两个数组合并后的第 (len >> 1) + 1 大的数,如果 len 为偶数,就是第 (len >> 1) 和 (len >> 1) + 1 两个数的平均数。

可以进一步扩展,给定两个有序数组,求第 k 大数。有序 + log 级别的复杂度,想到了什么?二分查找。

假设两个有序数组 a 和 b,长度分别是 m 和 n,求第 k 大数。假设在 a 中取 x 个,那么 b 数组中取的个数也就确定了,为 k - x 个,据此我们可以将两个数组分别一分为二,根据两边的边界值判断此次划分是否合理。而对于 x 的值,我们可以用二分查找。二分查找可以用迭代或者递归,这里我参考了 leetcode之 median of two sorted arrays 的递归写法,美中不足的是频繁调用了 slice 方法,可能导致性能下降。

var findMedianSortedArrays = function(nums1, nums2) {
  var m = nums1.length;
  var n = nums2.length;
  var total = m + n;
  var half = total >> 1;

  if (total & 1)
    return findKth(nums1, m, nums2, n, half + 1);
  else
    return (findKth(nums1, m, nums2, n, half) + findKth(nums1, m, nums2, n, half + 1)) / 2;
};

function findKth(a, m, b, n, k) {
  // always assume that m is equal or smaller than n
  if (m > n)
    return findKth(b, n, a, m, k);
  if (m === 0)
    return b[k - 1];
  if (k === 1)
    return Math.min(a[0], b[0]); 

  // divide k into two parts
  var pa = Math.min(k >> 1, m)
    , pb = k - pa;  

  if (a[pa - 1] < b[pb - 1])
    return findKth(a.slice(pa), m - pa, b, n, k - pa);
  else if (a[pa - 1] > b[pb - 1])
    return findKth(a, m, b.slice(pb), n - pb, k - pb);
  else
    return a[pa - 1];
}  

如果有其他解法或者建议,欢迎探讨~

【算法之美】求解两个有序数组的中位数 — leetcode 4. Median of Two Sorted Arrays的更多相关文章

  1. 求两个有序数组的中位数(4. Median of Two Sorted Arrays)

    先吐槽一下,我好气啊,想了很久硬是没有做出来,题目要求的时间复杂度为O(log(m+n)),我猜到了要用二分法,但是没有想到点子上去.然后上网搜了一下答案,感觉好有罪恶感. 题目原型 正确的思路是:把 ...

  2. 两个有序数列,求中间值 Median of Two Sorted Arrays

    原题: There are two sorted arrays nums1 and nums2 of size m and n respectively.Find the median of the ...

  3. python经典算法题目:找出这两个有序数组的中位数

    题目:找出这两个有序数组的中位数 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)). 你可以 ...

  4. PHP算法之寻找两个有序数组的中位数

    给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)). 你可以假设 nums1 和 nums2  ...

  5. Java算法练习——寻找两个有序数组的中位数

    题目链接 题目描述 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 $O(log(m + n))$. 你可以假设 nu ...

  6. [LeetCode] Median of Two Sorted Arrays 两个有序数组的中位数

    There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two ...

  7. [LeetCode] 4. Median of Two Sorted Arrays 两个有序数组的中位数

    There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two ...

  8. 求两个有序数组的中位数或者第k小元素

    问题:两个已经排好序的数组,找出两个数组合并后的中位数(如果两个数组的元素数目是偶数,返回上中位数). 设两个数组分别是vec1和vec2,元素数目分别是n1.n2. 算法1:最简单的办法就是把两个数 ...

  9. 寻找两个有序数组的中位数 C++实现leetcode系列(四)

    给定两个大小为 m 和 n 的有序数组 nums1和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)). 你可以假设 nums1 和 nums2 不 ...

随机推荐

  1. phonegap学习笔记

    [windows下安装] 1 先安装node.js: http://nodejs.org/ 2 CMD下运行: C:\> npm install -g phonegap [创建项目] CMD下运 ...

  2. ORACLE 查看RMAN的备份信息总结

    关于Oracle数据库的RMAN备份,除了邮件外,是否能通过其它方式检查RMAN备份的成功与失败呢?其实我们可以通过下面SQL脚本来检查某个时间段备份失败的记录: SELECT * FROM V$RM ...

  3. top:failed tty get 错误

    运行命令,ps -ef | grep test | grep -v test | awk '{ print $2 }' | xargs top -H -p 想看test的实时状态,结果报了错,查了一下 ...

  4. 利用apply和arguments复用方法

    首先,有个单例对象,它上面挂了很多静态工具方法.其中有一个是each,用来遍历数组或对象. var nativeForEach = [].forEach var nativeMap = [].map ...

  5. Seq_file文件系统实例剖析

    http://blog.chinaunix.net/uid-24432676-id-2607766.html 另 http://www.cnblogs.com/qq78292959/archive/2 ...

  6. linux 文件系统解析及相关命令

    简介 文件系统就是分区或磁盘上的所有文件的逻辑集合. 文件系统不仅包含着文件中的数据而且还有文件系统的结构,所有Linux 用户和程序看到的文件.目录.软连接及文件保护信息等都存储在其中. 不同Lin ...

  7. 学习OpenStack之(5):在Mac上部署Juno版本OpenStack 四节点环境

    0. 前沿 经过一段时间的折腾,终于在自己的Mac上装好了Juno版本的四节点环境.这过程中,花了大量的时间,碰到了许多问题,学到不少知识,折腾过不少其实不需要折腾的东西,本文试着来对这过程做个总结. ...

  8. [转]CISP(注册信息安全专业人员)认证(12天)

    本文转自:http://www.topsec.com.cn/shpx/rzpx/pxkc/cisp/index.htm CISP(注册信息安全专业人员)认证(11天) 中国信息安全产品测评认证中心(C ...

  9. MMORPG大型游戏设计与开发(客户端架构 part5 of vegine)

    客户端异常捕获,是一件必然的事情,特别是在开发的时候就更需要这些有利于找出问题原因的捷径.区别于服务器的是,客户端基本上是以界面为主,你很难在正常运行程序的情况下进行一些输出的监视,如一些日志的记录. ...

  10. UVA 10375 Choose and divide【唯一分解定理】

    题意:求C(p,q)/C(r,s),4个数均小于10000,答案不大于10^8 思路:根据答案的范围猜测,不需要使用高精度.根据唯一分解定理,每一个数都可以分解成若干素数相乘.先求出10000以内的所 ...