LeetCode 04寻找两个正序数组的中位数(困难)二分法
LeetCode 04寻找两个正序数组的中位数(困难)二分法
题目描述:
呕心沥血的一个题解,点赞关注收藏,一键三联,一起加入我们打卡!
题目描述:
给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 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(log(m+n))的方法,只能想到O(m+n)的归并,没想到怎么去使用二分,后来看了题解也是才明白。但也算自己理解了和大家分享一下。
对于这个问题或许本身不难,但是可能难在O(log(m+n))的时间复杂度上。
如果真的无法想到好的方法,先想着过关,该用什么方法呢?
法一暴力法:
可以将两个数组添加到一个总的数组中,然后给这个数组进行排序。正常的排序是O(nlogn)的时间复杂度。排序之后根据奇数偶数取中位数即可。
法二归并法:
给的两个数组本身是有序的,想必熟悉归并排序的朋友们应该能一下就想出来这个方法,两个有序的.只需按照以下流程即可完成归并:
待归并的两个数组分别设置两个指针leftindex,rightindex均从0开始。新数组也设置游标index。
比较两数组leftindex和rightindex位置的值,较小的那个赋值到新数组中同时新数组游标和较小的那个游标均加一。
在这里插入图片描述 重复到其中一个数组遍历完,另一个数组剩余值直接加入后面即可。
在这里插入图片描述
实现代码:
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int a[]=new int[nums1.length+nums2.length];
int lindex=0,rindex=0;
int index=0;
while (lindex<nums1.length&&rindex<nums2.length) {
if(nums1[lindex]<nums2[rindex])
{
a[index++]=nums1[lindex++];
}
else {
a[index++]=nums2[rindex++];
}
}
while (lindex<nums1.length) {
a[index++]=nums1[lindex++];
}
while (rindex<nums2.length) {
a[index++]=nums2[rindex++];
}
if(a.length%2==0)
return (double)(a[a.length/2-1]+a[a.length/2])/2;
else {
return a[a.length/2];
}
}
二分法(O(log(m+n)))
想到有序的,又是O(log(m+n))的时间复杂度,估计大部分人都能想到二分,我当时也是一样,但是该怎么想呢这就是一个问题。记录下我当初错误的想法:
二分,二分找到两个中间的。然后正常有个长的,有个短的,根据两个数值比较分类推测中位数应该在哪个区间……然后大脑就断电了。
对于中位数的简单分析:
如果两个数组长度和为奇数,那么最终这个中位数是由一位数确定的。
如果两个数组长度和为偶数,那么最终这个中位数是由两位数取平均值确定的。
对两个数组的简单分析:
两个数组应该有一个长一点,另一个点一点(等长也不影响)。
中位数可能让两个数组都分成两部分:一部分小于中位数,一部分大于中位数。但两个部分合起来总数量应该一致。
在这里插入图片描述
对两数组和中位数位置分析:
我们知道两数组虽然可能等长(不影响),但正常情况应该是一个长(m)一个短(n)。长短数组分别对应的坐标m1和n1和中位数坐标有什么关系?
无论总和奇数偶数,都满足(m1+n1)=(m+n)/2;因为两个数组都是有序的所以总共小于中位数的占一半。其中m和n是定值。也就是不管你怎么变动,这两个坐标编号是总和为定值得!
如何分析为定值得坐标
既然两个坐标的总和为定值,那么可不可以把其中一个当为自变量,一个看成自变量呢?比如x+y=5你不好分析但是y=5-x,你分析x同时y就确定了。对吧?
那么选择长的那个作为变量还是短的那个作为变量呢?短的,为啥,主要因为如果从长的当成变量咱们有些区域无法对应到短的(因为长度即使加上短的所有也到不了一半,处理起来麻烦):
在这里插入图片描述 但是短的就可以很好避免这种情况:
在这里插入图片描述
所以我们就用二分去查找小的这个区间,找到最终的结果,你可能会问:什么样情况能够满足确定这条线的附近就是产生中位数的?
二分进行查找编号的时候,满足左侧都比线右侧小才行。这种情况在二分查找就是一个平衡的结果。
在这里插入图片描述
最后找到这个index线了。取值比较你还要有注意的地方:
取左侧的时候左侧如果有index为0,取右侧的时候index为最大值:
在这里插入图片描述 所以这种在你最后取值的时候,需要考虑左右侧是否有值。同时取长的那个也要比较,因为可能出现等长情况例如:
1 2 3 4
,和5 6 7 8
这种去到临界。需要判断当然在实现过程用三目运算简化!
总的来说:
根据短的进行二分查找位置,先找到线index,说明中位数在附近产生。(奇数偶数在查找因为要除2可以通用表达式)
如果总个数奇数,那么就是线左侧最大的那个(两个比较或只有一个)
如果总个数偶数,那么就是线左侧最大的那个(两个比较或只有一个)和线右侧最小的那个(两个比较或只有一个)的值取平均,注意是double类型。
其他注意点,搞清index从0开始,搞清逻辑上的第几个和数组显示使用的第几个的index的区别。
附上代码:
public static double findMedianSortedArrays2(int[] nums1, int[] nums2) {
if(nums1.length>nums2.length)//保证num1长度小,如果不小我交换一下
{
int team[]=nums2.clone();
nums2=nums1;
nums1=team;
}
int k=(nums1.length+nums2.length+1)/2;//理论中位数满足的位置
int left=0,right=nums1.length;//二分查找短的
while (left<right) {//找到对应位置
int m1=(left+right)/2;//在短的位置
int m2=k-m1;//在长的第几个
//System.out.println(m1+" "+m2);
if(nums1[m1]<nums2[m2-1])//left右移
left=m1+1;
else {//right左移
right=m1;
}
}
//System.out.println(left+" "+k);
//左侧最大和右侧最小那个先算出来再说,根据奇偶再使用
double leftbig= Math.max(left==0?Integer.MIN_VALUE:nums1[left-1], k-left==0?Integer.MIN_VALUE:nums2[k-left-1]);
double rightsmall=Math.min(left==nums1.length?Integer.MAX_VALUE:nums1[left],k-left==nums2.length?Integer.MAX_VALUE:nums2[k-left]);
//System.out.println(rightsmall);
if((nums1.length+nums2.length)%2==0)
{
return (leftbig+rightsmall)/2;
}
else {
return leftbig;
}
}
结语
本次打卡结束,再接再励。
至于其他方法暂时先不学了,感觉这题还是挺有难度的,需要搞明白要点时候。
最近往期精彩回顾:
听说长得俊的更喜欢给人点赞在看关注,关注公众号:bigsai
,回复加群,咱们一起打卡,目前已经一百多位csdn朋友加入打卡。
长按识别
关注我们
本文分享自微信公众号 - bigsai(bigsai)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
使用 tail -f 实时观测服务器日志输出
在开发阶段, 有 console 端的输出, 总是可以方便实时地看到应用的日志. 可一旦应用部署到服务器上之后呢, 日志被输出到文件中, 在某些情景下需要不停地查看日志文件的输出以定位某些问题, 此时是否还能像开发那样实时查看日志呢? 答案是可以的! 这个命令就是 tail -f . tail -f 具体使用例子 来看一个具体的示例, 比如在我的服务器上, 想实时查看下 nginx 访问日志的情况, 我可以进入其日志文件夹, 里面有个 access.log, 每当有请求过来时, nginx 都会往里面记录日志: 然后使用以下的命令实时监测日志变化: tail -f access.log 之后刷新一下我个人网站的主页, 可以看到日志自动滚动了: tail -f 具体含义 首先简要介绍下 tail 命令. 通常日志文件都是比较大的, 而我们感兴趣的最新的日志部分又打印在最后, 而 tail 就是用于查看这些最新输出的日志. tail 是尾巴, 尾部的意思. 使用 tail --help 查看其帮助: 可以看到一个 -f, --follow 的选项, 其含义为: output appe...
- 下一篇
c++之重载函数学习总结
一、C++中的函数重载: 1、函数重载的概念: 用同一个函数名定义不同的函数 当函数名和不同的参数搭配时函数的含义不同 注意:在c语言中是没有函数重载这个概念的。 代码示例演示: #include<stdio.h>#include<string.h>intfunc(intx){returnx;}intfunc(inta,intb){return(a+b);}intfunc(constchar*s){returnstrlen(s);}intmain(){return0;} 上面在c++编译器里面编译时没有问题的,如果放在c语言编译器里面编译是会报错的: root@txp-virtual-machine:/home/txp#gcctest5.ctest5.c:8:5:error:conflictingtypesfor‘func’intfunc(inta,intb)^test5.c:3:5:note:previousdefinitionof‘func’washereintfunc(intx)^test5.c:13:5:error:conflictingtypesfor...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- 设置Eclipse缩进为4个空格,增强代码规范
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS6,CentOS7官方镜像安装Oracle11G
- Docker安装Oracle12C,快速搭建Oracle学习环境