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. 一定要在commit之前做RAR备份,这样在出问题的时候,可以排除别人代码的干扰

    否则找错实在是太痛苦了,根本不知道来自哪里...而这样上面那样做,可以节省时间.

  2. postgresql + JDBC 学习

    Based on debian 9, postgresql 9.6 and Java 8, at Dec-24-2018 ======================================= ...

  3. How to Use the Dynamic Link Library in C++ Linux (C++调用Delphi写的.so文件)

    The Dynamic Link Library (DLL) is stored separately from the target application and shared among dif ...

  4. Design Thinking Workshop @ Agile Tour 2013 Shanghai

    设计思维工作坊 上周日在2013年敏捷之旅上海站,引导分享了一个设计思维的工作坊.这个工作坊持续了3个小时.来篇流水账分享给大家. 我们的设计挑战是什么呢?左思右想,在准备设计挑战题目的时候纠结了好久 ...

  5. Jstack线程状态BLOCKED/TIMED_WAITING/WAITING解释

    一.线程5种状态 新建状态(New) 新创建了一个线程对象. 就绪状态(Runnable) 线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中,变得可运行,等待获 ...

  6. .NET架构师知识普及

    今天看到一篇漫画,[3年.NET开发应聘大厂惨遭淘汰,如何翻身打脸面试官?],好多问题,一下子还真的回答不了,这里对这些问题进行了整理,增加下脑容量,哈哈.俗话说不想当将军的士兵不是好士兵,不想当架构 ...

  7. C# Winfrom 简单的运用Timer控件

    注意,在使用DateAndTime时,需要添加引用 using Microsoft.VisualBasic;否则不可以计算时间之间的差值. using System; using System.Col ...

  8. MediatR一个.net中简单好用的中介者模式实现方案

    MediatRGit地址:https://github.com/jbogard/MediatR 1.安装妞盖特包 一般来说只需要安装一个MediatR就行了,.net core程序需要再安装一个Med ...

  9. RobotFramework + HTTP接口自动化实现

    一.          什么是自动化测试? 1.      定义 自动化测试是把以人为驱动的测试行为转化为机器执行的一种过程,也可以说是软件测试的一种技术手段. 2.      常见工具 Appium ...

  10. 常用的方法论-5why