https://leetcode-cn.com/problems/median-of-two-sorted-arrays/submissions/

我的解法:

我看到了那个log的要求,也第一时间想到了二分,但是实在想不出咋二分,最后只能写个暴力的...

 class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
//特判有一个数组为空的时候
if(m==0){
if(n%2==0)
return (nums2[n/2-1]+nums2[n/2])/2.0;
else
return nums2[n/2];
}
if(n==0){
if(m%2==0)
return (nums1[m/2-1]+nums1[m/2])/2.0;
else
return nums1[m/2];
}
int curr1=0,curr2=0;
double ans = 0,ans2 = 0;
for(int i=0;i<Math.ceil((m+n)/2.0)+1;++i){
ans = ans2;
//如果某个数组到尽头了
if(curr1>=m || curr2 >=n){
ans2 = curr1>=m?nums2[curr2++]:nums1[curr1++];
}else{
ans2 = nums1[curr1]<nums2[curr2]?nums1[curr1++]:nums2[curr2++];
}
}
if((m+n)%2==0)
ans = (ans + ans2)/2.0;
return ans;
}
}

中位数我理解为第k小的数,在长度为奇数时为k=ceil(len/2),在长度为偶数时则要求两个第k小的数再求他们的平均值,即k1=len/2,k2=len/2+1

说一下为啥我把特判长度为0写在最头部显得很臃肿,因为中位数的性质可以发现采用暴力方法时我们只需要遍历nums1与nums2长度和的一半个元素即可.

换句话说,除非两个数组全空,否则不可能出现两个数组同时到达末尾的情况.而题目已经把两个都空的情况排除了,所以只需要考虑一个空的情况

如果不像我这样把特判长度0写在最前面,那么就必须在循环里进行至少两个if去判断究竟是哪个数组走到了末尾,个人觉得这样的开销没有必要,

像现在这样用或判断数组越界结合长度0特判可以省掉很多无谓的if,给暴力这种原始方法带来些微的优化.

时间复杂度O((m+n)/2)=O(n),空间复杂度O(1)

执行错误若干次: 一开始没有很好的考虑某个数组空的情况,或者写法太不优雅导致难以检查出现了写错数组名的低级错误.

结果:

结果意外的还不错,可以发现LeetCode没有用数据去卡我们.只是题面要求做到O(log(n))时间复杂度而已.

官方题解:

https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-shu-b/

方法: 递归法(?,个人感觉跟递归没关系,是一种分治的思想)

我之前理解的中位数是第k小/第k大的数(取决于从哪个方向去看),这个题解里对中位数有了更妙的理解

官方题解写的很好,但是很多地方可能有点绕,不那么通俗,我来尝试通俗的解释一下.

为了方便,我们首先假设两个数组的长度m和n均是偶数(且不同时为0),一奇一偶和双奇数长度的情况我们在讨论完双偶数后对其适当变形即可

偶数长度个数的中位数是按照从小到大顺序排列的最中间两个数的平均,那么我们这样想: 在这两个数间画一条竖线,将整个数组分成两个大小相等的部分:

左侧有len/2个数,其中最右侧的最靠近这条竖线的数是要用来计算出中位数的一个数,类似的,右侧有len/2个数,其中最左侧的最靠近这条竖线的数也是要用来计算中位数的一个数

我们把左侧的半个数组称为left,右侧的称为right

由于整个数组都是有序的,遵循从小到大排的规则,那么left的最右侧的数就是left中所有数的最大值,right中的最左侧的数就是right中所有数的最小值.

因此整个数组的中位数就可以表示为 (max(left)+min(right))/2,这样表示有什么好处呢?

它实质上取消了left和right各自的有序性要求,即弱化了数组的有序性要求(注意不是取消了有序性要求),现在我们不需要保证整个数组都有序了!

我们只需要保证两个部分间有序即可,不必考虑内部: 被称作right的部分中的任意数大于等于被称作left的部分中的所有数即可!

例如,我们获得了一个无序的数组[6,4,5,3,1,2],但是不难发现如果拆分成[6,4,5][3,1,2]这两个部分就满足了我们前面所叙述的要求.

right = [6,4,5],left = [3,1,2](注意left指较小的部分,与这个部分实际物理上的"左"还是"右"无关,right同理),因此整个数组的中位数就应当是(4+3)/2 = 3.5! 容易验证这确是正确答案.

但是注意到我们现在都在针对一个数组来说的,我们现在有nums1,nums2两个数组,因此要把他们"合起来",

此处不是指物理上合并起来,而是说把它们当成一个数组考虑: 把nums1拆成两部分left_1与right_1,把nums2拆成两部分left_2与right_2,再把这四部分两两结合,

left_1与left_2结合成为虚构的整体数组的left,right_1与right_2结合成为虚构的整体数组的right,而且这个left和这个right满足我们前文的叙述.

当两数组均非空时可以证明必然存在这样的划分: 前文我们已经证明了一个弱化有序的数组是可以通过拆分方法求中位数的,显而易见,两个独立的有序数组必然可以经过O(m+n)的时间复杂度合并(此处指物理合并了,归并排序中那种合并)为一个有序的数组,而弱化有序是有序的必要条件,因此可知这个合并后的数组必然可以通过拆分法求中位数.拆分法中的left中任意数都小于等于right中的所有数,且left中的任意数不是来自nums1就是来自nums2,把left中的所有来自nums1的数组成的集合就是left_1,按照此位置拆分nums1即可.nums2同理,不再赘述.

让我来举个例子,不妨令 nums1 =  [-2,1,3,3,4,5,6,17,18,19], nums2 =  [0,1,4,4,8,9],他们的长度分别是10和6,正确的中位数应当是(4+4)/2=4,那么我们期望做这样一件事情:

step1. nums1 => left_1=[-2,1,3,3] ,right_1=[4,5,6,17,18,19]

step2. nums2 => left_2=[0,1,4,4] ,right_2=[8,9]

step3. left = left_1 并 left_2 = [-2,1,3,3

               0,1,4,4] , left长8

step4. right = right_1 并 right_2 = [4,5,6,17,18,19

                8,9] , right长8

(step4.5. 验证right中任意数确大于等于left中所有数)

step5. 中位数 = (max(left)+min(right))/2 = (4+4)/2 = 4   ✔

到此为止,我们将问题转换成了: 如何找到正确的划分的位置,使得step4.5中的验证能正确?

我们用朴素的思维想一个暴力的想法: nums1的长度为m,根据简单的排列组合知识,砍一刀把它分成两个部分有C(1,m+1) = m+1 种方法,类似的nums2有n+1种拆分方法

我们把nums1拆分的位置称为i,nums2拆分的位置称为j,i属于[0,m],j属于[0,n],因此似乎我们只需要开两重循环暴力找到i和j就可以了?

慢着,我们这样浪费了太多信息了,而且复杂度也高达O(n2).别忘了left和right是等长的!在我们的假设里(m与n均为偶数)不难发现i与j的关系:

left长度 =  right长度

i + j       =  m - i + n - j

即j = (m + n - 2*i)/2,假设保证了他是一个整数.注意到i与j的关系为i增j减,i减j增.为了使j是一个正数,我们应当使n大于等于m

因此我们减少了一重循环,只需要开i的循环即可.复杂度降到了O(n).到了这个时候就不难看出题目要求的O(log(n))应该在哪里二分了,就是在i的搜索中.

我们已知i属于[0,m],那么我们首先查找i=m/2时是否满足"弱化有序"的条件.因为我们从原来真正有序的数组上拆分,因此left_1天然的对right_1满足弱化有序.同理left_2对right_2也是.

所以我们只需要手动检查:

1. left_1中的最大值是否小于right_2中的最小值  下简称条件1

2. left_2中的最大值是否小于right_1中的最小值  下简称条件2

之后选择二分的[0,m/2)部分还是[m/2,m]部分呢?

我们考察一下i=m/2时,如果不满足条件1,说明划给left_1的数太多了,即i太大了,因此应当减小i,选择[0,m/2)部分继续二分

如果不满足条件2,说明划给left_2的数太多了,即j太大了,由i与j的关系,即i太小了,因此应当放大i,选择[m/2,m]部分继续二分

至此形成了完整的思路了,代码稍后奉上.

 class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
if(n<m){
int[] temp = nums1;
nums1 = nums2;
nums2 = temp;
int tempnum = m;
m = n;
n = tempnum;
}
int i, j;
int up_limit = m;
int down_limit = 0;
while (true) {
i = (up_limit + down_limit) / 2;
j = (m + n - 2 * i) / 2;
if (((0 < i && i <= m) ? nums1[i - 1] : Integer.MIN_VALUE) <= ((0 <= j && j < n) ? nums2[j]
: Integer.MAX_VALUE)) {
if (((0 < j && j <= n) ? nums2[j - 1] : Integer.MIN_VALUE) <= ((0 <= i && i < m) ? nums1[i]
: Integer.MAX_VALUE)) {
break;
} else {
down_limit = i + 1;
}
} else {
up_limit = i - 1;
}
}
if((m+n)%2==0)
return (Math.max(((0 < i && i <= m) ? nums1[i - 1] : Integer.MIN_VALUE),
((0 < j && j <= n) ? nums2[j - 1] : Integer.MIN_VALUE))
+ Math.min(((0 <= j && j < n) ? nums2[j] : Integer.MAX_VALUE),
((0 <= i && i < m) ? nums1[i] : Integer.MAX_VALUE)))
/ 2.0;
else
return Math.min(((0 <= j && j < n) ? nums2[j] : Integer.MAX_VALUE),
((0 <= i && i < m) ? nums1[i] : Integer.MAX_VALUE));
}
}

时间复杂度O(log(n)),空间复杂度O(1).

结果: 

具体解说与注释明天补上,累了先休息了.

2019年7月22日 - LeetCode0004的更多相关文章

  1. 2019年7月22日A股科创板开板首日行情思考

    2019年7月22日A股科创板开板首日行情思考 原因:2019科创板开板交易 盘面:科创板交易活跃,首批上市25只股票大涨,最高达5倍涨幅:主板交投低迷,量能萎缩,大部分股票下跌. 操作:加仓 西安银 ...

  2. 2019年4月22日A股暴跌行情思考

    2019年4月22日A股暴跌行情思考 原因:股指期货松绑 盘面:小幅高开,单边下跌 操作: 总结: 股指期货松绑,周末媒体YY大盘暴涨,不排除空头故意借助媒体来诱多,然开盘后暴跌. 预期过于一致时,需 ...

  3. 2019年5月22日 AY 程序员调侃语录

    我是AY,杨洋,做wpf开发的,最近得了一种病,程序员患得患失综合征.同事说,我年纪在变大,技术跟不上.业余之间,我原创了写了一些语录,给大家中午休息,累疲惫的时候,开心放松下. 1.活着的每一天都无 ...

  4. 【2019年07月22日】A股最便宜的股票

    查看更多A股最便宜的股票:androidinvest.com/CNValueTop/ 便宜指数 = PE + PB + 股息 + ROE,四因子等权,数值越大代表越低估. 本策略只是根据最新的数据来选 ...

  5. 【2019年04月22日】A股最便宜的股票

      太钢不锈(SZ000825) - 当前便宜指数:170.67 - 滚动扣非市盈率PE:4.37 - 滚动市净率PB:0.98 - 动态年化股息收益率:4.79%- 太钢不锈(SZ000825)的历 ...

  6. 永久免费开源的卫星地形图地图下载工具更新Somap2.13版本功能更新 更新时间2019年2月22日13:59:05

    一.下载地址 最新版本下载地址:SoMap2.13点击此处下载  二.系统自主开发特色功能展示 1.上百种地图随意下载 高德.百度.arcgis.谷歌.bing.海图.腾讯.Openstreet.天地 ...

  7. 2019年8月22日 星期四(怎样成为PHP大牛)

    1.服务器方面,各种PHP部署方案烂熟,Lvs,keepalived,nginx,apache,docker,换句话说其战力值相当于一个高级运维,迅速定位并排除PHP运行中的各种问题. 2.数据库方面 ...

  8. AHKManager.ahk AHK管理器 2019年12月15日

    AHKManager.ahk  AHK管理器  2019年12月15日 快捷键   {Alt} + {F1} ///////////////////////////////////////////// ...

  9. ;~ 小部分AutoHotkey源代码片段测试模板2019年10月9日.ahk

    ;~ 小部分AutoHotkey源代码片段测试模板2019年10月9日.ahk ;~ 此脚本用于测试执行一行或多行AHK脚本源代码的效果;~ 此脚本最后修改于2019年9月22日20时03分;~ 把此 ...

随机推荐

  1. Qt 开发WEB Services客户端代码(使用gSoap)

    1.   首先下载gSoap开发包 http://sourceforge.net/projects/gsoap2  目录包含 wsdl2h.exe( 由wsdl生成接口头文件C/C++格式的头文件 ) ...

  2. CPU多核控速

    初学者很多对自己开发的软件使用硬件资源的时候并不注意,造成写出的东西不是很满意. 一般有两种情况: 1.写的都是同步单线程任务,不管你电脑有多少个核都不关我事 我就用你1个核所以不管怎么样都不会把CP ...

  3. Qt之OpenSSL(有pro文件的路径格式,以及对libeay32和ssleay32的引用)

    简述 OpenSSL是一个强大的安全套接字层密码库,囊括主要的密码算法.常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用. 简述 下载安装 使用 更多参考 下载安装 ...

  4. 从 docker 到 runC

    笔者在前文<RunC 简介>和<Containerd 简介>中分别介绍了 runC 和 containerd.本文我们将结合 docker 中的其它组件探索 docker 是如 ...

  5. windows环境利用hexo+github搭建个人博客

    一.下载安装Git 下载地址:https://gitforwindows.org/ 二.下载安装node.js 下载地址:https://nodejs.org/en/ 三.安装hexo 利用 npm ...

  6. java里字节与字符的区别

    当时学Java的时候没搞懂字节和字符的区别,今天看文件输入输出流的时候觉得是时候彻底把这两个概念弄懂. 首先得知道byte的概念和作用: byte即字节的意思,是java中的基本数据类型,用来申明字节 ...

  7. 长春理工大学第十四届程序设计竞赛(重现赛)H

    H .Arithmetic Sequence 题目链接:https://ac.nowcoder.com/acm/contest/912/H 题目 数竞选手小r最喜欢做的题型是数列大题,并且每一道都能得 ...

  8. Linux运维工程师学习成长路线

    不过大家的留言都很精彩,希望大家也可以去留言区逛一逛~~ 好在这不是最后一期送书,之前已经有了好多活动,小编一定继续为大家多送些福利. 希望大家可以一如既往的关注脚本之家,支持爱你们的小编,共同进步! ...

  9. 常用的方法论-SWOT

  10. hihoCoder 1308:搜索二·骑士问题(BFS预处理)

    题目链接 题意 中文题意. 思路 对于每一个骑士,可以先预处理出到达地图上某个点的需要走的步数,然后最后暴力枚举地图上每一个点,让三个骑士走过的距离之和最小即可. #include <bits/ ...