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. scons编译mongodb(vs2008版本)遇到的问题总结

    OS:win7 64 boost:1.49 mongodb:2.4.6(推荐64位版本,当然如果你系统是32位的,只能使用32的版本了) IDE:vs2008(2010的同学请跳过吧,因为官网提供的就 ...

  2. fprintf函数将格式打印到文件,非常好用(怎么没早点发现这个函数)

    /* fprintf example */ #include <stdio.h> int main () { FILE * pFile; int n; ]; pFile = fopen ( ...

  3. Elasticsearch教程(一)简介与安装

    简单概念 ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作为 ...

  4. NFS服务设置

    1.安装NFS服务sudo apt-get install nfs-common nfs-kernel-server 2.配置NFS服务首先需要手动编辑/etc/exports配置文件 权限参数说明如 ...

  5. Windows Python虚拟环境配置(Distribute + pip + virtualenv + virtualenvwrapper-powershell)

    对于Python开发新手,很多人会迷茫那些各种名目的工具和概念,如Python2.7, Python3.3, Distribute, pip, virtualenv,Setuptools, easy_ ...

  6. 更改当前电源策略(使用SetActivePwrScheme API函数),自定义电源按钮动作(设置GLOBAL_POWER_POLICY)

    #include <windows.h> #include <Powrprof.h> #pragma comment(lib, "Powrprof.lib" ...

  7. qt---cdb(Microsoft Console Debugger)调试

    支持的调试器 windows系统下主要的调试器: CDB ,只能调试用户程序,只有控制台界面,以命令行形式工作 NTSD, 只能调试用户程序,只有控制台界面,以命令行形式工作 KD,主要用于内核调试, ...

  8. Linux软件安装及基本概念

    apt 基本用法 apt-get [options] install/remove/source 软件包1 [软件包2...] 注意:软件包不要带后缀.deb 常用命令及解释如下: apt下载软件是根 ...

  9. Hyperledger Fabric1.4的多机部署

    之前的文章深入解析Hyperledger Fabric启动的全过程主要讲解了Fabric的网络搭建,以及启动的整体流程,但是都是通过单机完成的.而区块链本身就是去中心化的,所以最终还是要完成Fabri ...

  10. 曹工说Tomcat2:自己撸一个简易Tomcat Digester

    一.前言 框架代码其实也没那么难,大家不要看着源码就害怕,现在去看 Tomcat 3.0的代码,保证还是看得懂一半,照着撸一遍基本上很多问题都能搞定了.这次我们就模拟 Tomcat 中的 Digest ...