我现在在做一个叫《leetbook》的开源书项目,把解题思路都同步更新到github上了,需要的同学可以去看看
地址:https://github.com/hk029/leetcode
这个是书的地址:https://hk029.gitbooks.io/leetbook/

(这也是一道leetcode的经典题目:《LeetCode》解题笔记:004. Median of Two Sorted Arrays[H]

问题介绍

这是个超级超级经典的分治算法!!这个问题大致是说,如何在给定的两个有序数组里面找其中的中值,或者变形问题,如何在2个有序数组数组中查找Top K的值(Top K的问题可以转换成求第k个元素的问题)。这个算法在很多实际应用中都会用到,特别是在当前大数据的背景下。

我觉得下面的这个思路特别好,特别容易理解!!请按顺序看。是来自leetcode上的stellari英文答案,我整理并自己修改了一下。

预备知识

先解释下“割”

我们通过切一刀,能够把有序数组分成左右两个部分,切的那一刀就被称为割(Cut),割的左右会有两个元素,分别是左边最大值和右边最小值。

我们定义L = Max(LeftPart),R = Min(RightPart)

Ps. 割可以割在两个数中间,也可以割在1个数上,如果割在一个数上,那么这个数即属于左边,也属于右边。(后面讲单数组中值问题的时候会说)

比如说[2 3 5 7]这个序列,割就在3和5之间
[2 3 / 5 7]
中值就是(3+5)/2 = 4

如果[2 3 4 5 6]这个序列,割在4上,我们可以把4分成2个
[2 3 (4/4) 5 7]
中值就是(4+4)/2 = 4

这样可以保证不管中值是1个数还是2个数都能统一运算。

割和第k个元素

对于单数组,找其中的第k个元素特别好做,我们用割的思想就是:

常识1:如果在k的位置割一下,然后A[k]就是L。换言之,就是如果左侧有k个元素,A[k]属于左边部分的最大值。(都是明显的事情,这个不用解释吧!)

双数组

我们设:
Ci C_i为第i个数组的割。
Li L_i为第i个数组割后的左元素.
Ri R_i为第i个数组割后的右元素。

如何从双数组里取出第k个元素

  1. 首先 Li<=Ri L_i 是肯定的(因为数组有序,左边肯定小于右边)
  2. 如果我们让 L1<=R2 L_1 && L2<=R1 L_2

  1. 那么左半边 全小于右半边,如果左边的元素个数相加刚好等于k,那么第k个元素就是Max(L1,L2),参考上面常识1。
  2. 如果 L1>R2,说明数组1的左边元素太大(多),我们把C1减小,把C2增大。L2>R1同理,把C1增大,C2减小。

假设k=3

对于
[1 4 7 9] [1\ 4\ 7\ 9]
[2 3 5] [2\ 3\ 5]

设C1 = 2,那么C2 = k-C1 = 1
[1 4/7 9] [1\ 4/ 7\ 9]
[2/3 5] [2/3\ 5]

这时候,L1(4)>R2(3),说明C1要减小,C2要增大,C1 = 1,C2=k-C1 = 2
[1/4 7 9] [1/4\ 7\ 9]
[2 3/5] [2\ 3/5]

这时候,满足了 L1<=R2 L_1 && L2<=R1 L_2 ,第3个元素就是Max(1,3) = 3。

如果对于上面的例子,把k改成4就恰好是中值。

下面具体来看特殊情况的中值问题。

双数组的奇偶

中值的关键在于,如何处理奇偶性,单数组的情况,我们已经讨论过了,那双数组的奇偶问题怎么办,m+n为奇偶处理方案都不同,

让数组恒为奇数

有没有办法让两个数组长度相加一定为奇数或偶数呢?

其实有的,虚拟加入‘#’(这个trick在manacher算法中也有应用),让数组长度恒为奇数(2n+1恒为奇数)。
Ps.注意是虚拟加,其实根本没这一步,因为通过下面的转换,我们可以保证虚拟加后每个元素跟原来的元素一一对应

之前 len 之后 len
[1 4 7 9] 4 [# 1 # 4 # 7 # 9 #] 9
[2 3 5] 3 [# 2 # 3 # 5 #] 7

映射关系

这有什么好处呢,为什么这么加?因为这么加完之后,每个位置可以通过/2得到原来元素的位置。

/ 原位置 新位置 除2后
0 1 0 1
5 2 5 2

在虚拟数组里表示“割”

不仅如此,割更容易,如果割在‘#’上等于割在2个元素之间,割在数字上等于把数字划到2个部分。

奇妙的是不管哪种情况:

Li = (Ci-1)/2
Ri = Ci/2

例:
1. 割在4/7之间‘#’,C = 4,L=(4-1)/2=1 ,R=4/2=2
刚好是4和7的原来位置!
2. 割在3上,C = 3,L=(3-1)/2=1,R=3/2 =1,刚好都是3的位置!


剩下的事情就好办了,把2个数组看做一个虚拟的数组A,目前有2m+2n+2个元素,割在m+n+1处,所以我们只需找到m+n+1位置的元素和m+n+2位置的元素就行了。
左边:A[m+n+1] = Max(L1+L2)
右边:A[m+n+2] = Min(R1+R2)

Mid = (A[m+n+1]+A[m+n+2])/2
= (Max(L1+L2) + Min(R1+R2) )/2

至于在两个数组里找割的方案,就是上面的方案。

分治的思路

有了上面的知识后,现在的问题就是如何利用分治的思想。

怎么分?

最快的分的方案是二分,有2个数组,我们对哪个做二分呢?
根据之前的分析,我们知道了,只要C1或C2确定,另外一个也就确定了。这里,为了效率,我们肯定是选长度较短的做二分,假设为C1。

怎么治?

也比较简单,我们之前分析了:就是比较L1,L2和R1,R2。
- L1>R2,把C1减小,C2增大。—> C1向左二分
- L2>R1,把C1增大,C2减小。—> C1向右二分

越界问题

如果C1或C2已经到头了怎么办?
这种情况出现在:如果有个数组完全小于或大于中值。可能有4种情况:
- C1 = 0 —— 数组1整体都比中值大,L1 >R2,中值在2中
- C2 = 0 —— 数组1整体都比中值小,L1

代码

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {int n = nums1.size();int m = nums2.size();if(n > m)   //保证数组1一定最短return findMedianSortedArrays(nums2,nums1);int L1,L2,R1,R2,c1,c2,lo = 0, hi = 2*n;  //我们目前是虚拟加了'#'所以数组1是2*n长度while(lo <= hi)   //二分{c1 = (lo+hi)/2;  //c1是二分的结果c2 = m+n- c1;L1 = (c1 == 0)?INT_MIN:nums1[(c1-1)/2];   //map to original elementR1 = (c1 == 2*n)?INT_MAX:nums1[c1/2];L2 = (c2 == 0)?INT_MIN:nums2[(c2-1)/2];R2 = (c2 == 2*m)?INT_MAX:nums2[c2/2];if(L1 > R2)hi = c1-1;else if(L2 > R1)lo = c1+1;elsebreak;}return (max(L1,L2)+ min(R1,R2))/2.0;}
};

【分步详解】两个有序数组中的中位数和Top K问题相关推荐

  1. 求两个有序数组中的中位数和第k小的元素

    我们首先来看一下这一类题算法的原型: 对应牛客网链接: https://www.nowcoder.com/practice/08588d568e164e0a9958b5d6a3c351f5?tpId= ...

  2. 寻找两个有序数组中的中位数

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

  3. LeetCode4.python实现:寻找两个有序数组中的中位数问题☆☆☆

    目录 问题 解题思路 python具体实现 题外话 问题 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log( ...

  4. 找两个有序数组中的中位数---分治法实验1

    问题描述 设X[ 0 : n - 1]和Y[ 0 : n – 1 ]为两个数组,每个数组中含有n个已排好序的数.找出X和Y的2n个数的中位数. 编程任务 利用分治策略试设计一个O (log n)时间的 ...

  5. 用四种方法Python求出两个有序数组中的中位数

    方法一: def median_1(A, B):# 思路一: 先组合成一个有序数列,再取中位数# 时间复杂度O(m+n)len_A = len(A)len_B = len(B)C = []if len ...

  6. 取出两个有序数组里面的公共元素 java_C语言计算两个有序数组中的公共元素

    求两个有序数组的共同元素,比如数组a={1,2,3,8,9}和b={8,9,10},则输出output={8,9} // 找出两个数组的共同元素 int* FindCommon(int* a, int ...

  7. java有序数组找中位数_有序数组中找中位数

    题目:两个有序数组A和B,大小都是n,寻找这两个数组合并后的中位数.时间复杂度为O(logn). 中位数:如果数组的个数是奇数,那么中位数的值就是有序时处于中间的数:如果数组个数是偶数的,那么就是有序 ...

  8. 最详解——寻找两个有序数组的中位数

    参考:[分步详解]两个有序数组中的中位数和Top K问题 题目描述 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O ...

  9. LeetCode4寻找两个有序数组的中位数(二分查找+分治)

    目录 1.题目 2.分析 3.代码 二分查找:绝大多数二分查找问题利用的是单调性,也有一些例外)或者题目本身蕴含的可以逐渐缩小问题规模的特性解决问题.时间复杂度log级. 分治法的设计思想是:将一个难 ...

最新文章

  1. python创建列向量_关于Numpy中的行向量和列向量详解
  2. 5. python Collections -- OrdereDict
  3. bzoj1833: [ZJOI2010]count 数字计数USACO37 Cow Queueing 数数的梦(数位DP)
  4. linux环境下vim创建java文件,并编译运行
  5. java 数据结构详解,数组,集合,HashMap
  6. 4.聚合aggregate
  7. C程序对整数中设置为1的位数进行计数
  8. 小小一行Python命令,居然把电脑变成服务器
  9. php中dialog使用方法,window.dialogArguments 使用说明
  10. 计算机绘图中级,计算机绘图(中级)
  11. 从《硅谷传奇》看微软和苹果
  12. Spring系列缓存注解@Cacheable @CacheEvit @CachePut 使用姿势介绍
  13. Android通过百度api地址经纬度获取文字位置信息
  14. torch.sqrt(lhs**2) 与torch.abs(lhs)在pytorch中的差异
  15. 应届生如何成功进入产品经理岗位
  16. 单片机实验(十五)74LS47数码管译码
  17. SpringApplicationBuilder
  18. JavaWeb-03
  19. Java状态码枚举类
  20. CRM平台十点功能帮助企业提高销售营销技巧(下)

热门文章

  1. WEB网页的一些常识性见解
  2. 新闻系统开发摘要_每日新闻摘要:Microsoft提示使用新的“现代操作系统”
  3. CSS基础知识(十六):动画
  4. c语言提供了子程序的概念,C语言基本概念(1)
  5. MiniGUI字体放大、控件字体颜色的问题(转)
  6. 《花束般的恋爱》:用力绽放却注定凋零
  7. KD7742耐压接地泄漏绝缘四合一并行测试仪
  8. ntp服务器时钟同步
  9. 20170224找女朋友之路思考总结
  10. eclipse自动补全及其空格键优化(去除空格自动补全)