之前讲解过一道数据流求中位数的题目,但是仔细一想觉得那一次对几种数据结构简单的分析了一下实现,并没有对中位数的题目做一个凝练总结,这一次借这个机会,好好整理一下思路。

题目描述

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

  • 示例 1:
    nums1 = [1, 3]
    nums2 = [2]
    则中位数是 2.0
  • 示例 2:
    nums1 = [1, 2]
    nums2 = [3, 4]
    则中位数是 (2 + 3)/2 = 2.5

算法分析

  看到这个题目我们先来估计一下算法复杂度的上界,实在不行,我们就把两个有序数组合并成一个,然后去找中位数,这个合并局类似于归并排序归并的过程,显然这个复杂度是O(m+n)O(m+n)O(m+n)的,我们思考一下有没有其他更好的解决方式。
  再回顾一下上次强调的点,中位数我们最多只需要两个数即可,这两个数将整个序列分成了两段,如果顺便把这两个数归到这两段里,第一个数是前一段中最大的,第二个数是后一段中最小的。对于这种问题,我们使用排序也罢,使用partition函数找这个分段点也罢,总之就可以看成找一个分段位置,把这个数组分成合适的两段。
  我们继续沿用这个思路,之前是分一个数组,这次是分两个数组,但是问题是一致的,不过这里可能就是要找两个位置,把数组分成两段。而且这两个切分位置有一个很强的关联关系就是分割出来左边的数据之和是个定值。所以两个值的自由度其实还是1,相当于我们只找一个数。在有序数组中找一个数,那显然就是二分查找了。
  我们来分析一下这个定值是多少(计数从0开始),两种情况,m+n是偶数,计算中位数需要两个位置,(m+n)/2−1(m+n)/2-1(m+n)/2−1和(m+n)/2(m+n)/2(m+n)/2。如果是奇数,则是第(m+n)/2(m+n)/2(m+n)/2个。如果是奇数,我们让左边半部分多一个数字,但是如果我们利用上面的索引计算,第(m+n)/2(m+n)/2(m+n)/2个数字会被归到右半部分。这个时候就需要用一个技巧,利用计算机出发向下取整的原理,我们找第(m+n+1)/2−1(m+n+1)/2-1(m+n+1)/2−1和第(m+n+1)/2(m+n+1)/2(m+n+1)/2个数字就解决了。
  我们用两个数字i,j把两个数组分成两部分,显然j=(m+n)/2−ij=(m+n)/2-ij=(m+n)/2−i,我们的i是找第一个数组中的第 i 个元素,但是下标从0开始,我们实际上要找i-1的元素,j 也是同理。

  二分查找,我们先找一个位置,然后判断是否满足条件,不满足就继续划分,条件就是是否左半部分的数字都小于右半部分,显然i-1位置的数字肯定是小于i的,j-1也是小于j的,所以我们只要判断i-1位置的数字是否小于j的数字,j-1位置的数字是否小于i的。如果i-1位置的数字不小于j的数字,那么说明 i 应该左移,j应该右移。至于左移右移当然是直接使用二分查找就好。如果j-1位置的数字大于i,说明应该增大i,i右移(事实上改变了i, j就会相应改变)。
  理清了这些思路,就是复杂的边界处理了。第一种情况。如果i或j已经左移到0了,这个时候不能继续左移了。因为我们是二分查找,每次左移右移一半的情况,当i==0的时候无法左移。但是这时候j=(m+n)/2−i=(m+n)/2j=(m+n)/2-i=(m+n)/2j=(m+n)/2−i=(m+n)/2就是我们要找的数字,另一个我们要找的数字就是j-1了。同理 j=0,i就是我们要找的数字。


  但是如果i和j不能右移了怎么办,如果 i 不能右移说明i=m了,且nums1[m-1]<nums2[j],这个时候也说明 j 已经符合要求了。这里 j 不能右移也是同理。但是我们还可以使用一个小技巧,就是如果nums1比nums2长就交换,让nums的长度大于等于(m+n)/2(m+n)/2(m+n)/2,这样就不会出现j的无法右移的情况。

  可以看到核心代码还是一个二分查找,不过就是问题的边界情况处理更多一些了。需要注意的就是边界情况处理好,这个问题就解决了,下面贴出这个题的递归代码和非递归代码。

递归代码

class Solution1:def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:m,n=len(nums1),len(nums2)if m>n:nums1,nums2=nums2,nums1mid=(len(nums1)+len(nums2)+1)>>1median=self.getMedian(nums1,nums2,0,len(nums1),mid)return mediandef getMedian(self, nums1, nums2, start, end,mid):split1 =(start+end)>>1split2 = mid - split1if split1<end and nums1[split1]<nums2[split2-1]:return self.getMedian(nums1,nums2,split1+1,end,mid)elif split1>start and nums1[split1-1]>nums2[split2]:return self.getMedian(nums1,nums2,start,split1-1,mid)else:if split1==0:maxLeft=nums2[split2-1]elif split2==0:maxLeft=nums1[split1-1]else:maxLeft=max(nums2[split2-1],nums1[split1-1])if (len(nums1)+len(nums2))%2:return maxLeftif(split1==len(nums1)):minRight=nums2[split2]elif split2==len(nums2):minRight=nums1[split1]else:minRight=min(nums2[split2],nums1[split1])return (maxLeft+minRight)/2.0

  这个代码略微有点长,最关键的就是nums1是比较短的数组,split1<end的话,则说明长的一定不会比0小,split2-1是有意义的,因为split1+split2是个定值,所以只需要判断一个条件即可。判断 split1>start也是一个道理。else里面的一堆判断条件都是处理边界情况的。上面已经解释的很详细了。

循环代码

  这个二分查找的递归很好改成循环,我一并贴出来。

class Solution:def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:m,n=len(nums1),len(nums2)if m>n:nums1,nums2=nums2,nums1m,n=n,mmid=(len(nums1)+len(nums2)+1)>>1start=0end=len(nums1)while True:split1 =(start+end)>>1split2 = mid - split1if split1<end and nums1[split1]<nums2[split2-1]:start=split1+1elif split1>start and nums1[split1-1]>nums2[split2]:end=split1-1else:if(split1==0):maxLeft=nums2[split2-1]elif split2==0:maxLeft=nums1[split1-1]else:maxLeft=max(nums2[split2-1],nums1[split1-1])if (m+n)&1:return maxLeftif(split1==m):minRight=nums2[split2]elif split2==n:minRight=nums1[split1]else:minRight=min(nums2[split2],nums1[split1])return (maxLeft+minRight)/2.0return 0.

总结分析

  可以看出上述代码是个二分查找,我们每次只二分查找找nums1中数据,需要注意到我们保证了nums1是长度较短的那个,所以这个复杂度就是O(logmin(m,n))O(log\ min(m,n))O(log min(m,n)),这个算法的复杂度就很让人满意了。
  最后对这种问题做一个总结,首先我们在求解中位数类的问题时,首先需要想到的一种方式就是我们如何合适的去找一个分割,左边找最大,右边找最小。能够把问题处理成分割问题,然后再对应找方法即可。
  最后再强调那句,看到有序数组(位置)的查找,先想二分法。看到复杂度logloglog,这个时候就想分治。

寻找两个有序数组的中位数相关推荐

  1. 算法--------------------寻找两个有序数组的中位数

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

  2. 两个有序数组的中位数 python_Python寻找两个有序数组的中位数实例详解

    Python寻找两个有序数组的中位数 审题: 1.找出意味着这是一个查找算法题 2.算法复杂度log级别,就是提示你是二分查找 3.二分查找实现一般为递归 (1)递归包括递归体 (2)终止条件 思路: ...

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

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

  4. 20191016:(leetcode习题)寻找两个有序数组的中位数

    寻找两个有序数组的中位数 题目 大致思路 代码实现 题目 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log( ...

  5. (JS)寻找两个有序数组的中位数

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

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

    Python寻找两个有序数组的中位数 审题: 找出意味着这是一个查找算法题 算法复杂度log级别,就是提示你是二分查找 二分查找实现一般为递归 (1)递归包括递归体 (2)终止条件 思路: 定理: 有 ...

  7. 力扣寻找两个有序数组的中位数

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

  8. LeetCode(Python实现)—寻找两个有序数组的中位数

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

  9. 分享一道力扣困难题~寻找两个有序数组的中位数(Java)

    目录 解题思路1 具体代码 解题思路2 具体代码 题目描述:给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2.请你找出并返回这两个正序数组的 中位数 .算法的时间复杂 ...

  10. 【LeetCode】寻找两个有序数组的中位数【性质分析+二分】

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

最新文章

  1. axure 模板_《惢客创业日记》2019.09.03(周二) 用Axure管理项目流程
  2. python画曲线图例-Python画各种图
  3. sqrtm--矩阵的平方根
  4. Lesson 3.张量的广播和科学运算
  5. 深度对比Python的3种“字符串格式化”方法,看看你喜欢哪一种?
  6. 实现一个通用的生产者消费者队列(c语言版本)
  7. 读书笔记--Head First C#目录
  8. 硬盘安装Linux救援系统,通过急救系统里往硬盘里安装 alpine linux
  9. 返回固定数据的web服务器
  10. mac和win电脑在同一局域网下互传文件
  11. MySQL事务隔离及锁机制
  12. 中国大学生学习与发展追踪研究(2007年至今)与中国综合社会调查(2003-2017年)与中国社会状况综合调查(2006-2019年)
  13. 题解 2020级HAUT新生周赛(二)
  14. 三次握手与四次挥手的爱恨情仇
  15. 《算法竞赛》获得清华大学出版社最受读者欢迎图书奖、CSDN十大年度IT图书奖
  16. 《通关!游戏设计之道》笔记(二) 游戏是哪些人做的
  17. [C语言]逆序一个字符串的内容
  18. Git merge时使用--no-ff参数
  19. STM32入门开发--LED模块实现跑马灯
  20. F1意大利站,阿隆索在法拉利的主场夺冠

热门文章

  1. Assets Pricing 资产定价(四)
  2. 北京工业大学计算机科学与技术考研真题,北京工业大学计算机科学与技术考研...
  3. 数学分析教程史济怀练习8.1
  4. Nginx配置文件详解说明
  5. 关闭VS里的C++智能提示
  6. 硬件设计40之什么是PLA、NTSC?
  7. 艾默生充电桩15kw模块,软件源码加原理图BOM 艾默生充电桩15kw模块原版软件源码含核心算法
  8. 【吐槽贴】项目经理如何进行高效沟通?
  9. 联想服务器开启虚拟化设置,联想电脑开启虚拟化 电脑肿么开启虚拟化
  10. 幸福的瑞星卡卡狮 郁闷的江民防火墙