小知识

INT_MIN在标准头文件limits.h中定义。

#define INT_MAX 2147483647
#define INT_MIN (-INT_MAX - 1)

题解思路

其实是类似的二分,优点在于通过添加 '#' ,实现更方便的二分。

题目:
给定两个大小为 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

算法:
为了解决这个问题,我们需要理解 “中位数的作用是什么”。在统计中,中位数被用来:

将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。

这其中又分为偶数组和奇数组:

奇数组: [2 3 5] 对应的中位数为3

偶数组: [1 4 7 9] 对应的中位数为 (4 + 7) /2 = 5.5

先解释下“割”
我们通过切一刀,能够把有序数组分成左右两个部分,切的那一刀就被称为割(Cut),割(Cut)的左右会有两个元素,分别是左边最大值和右边最小值。

我们定义LMax= Max(LeftPart),RMin = Min(RightPart)。

割可以割在两个数中间,也可以割在1个数上,如果割在一个数上,那么这个数即属于左边,也属于右边

奇数组: [2 3 5] 对应的中位数为3,假定割(Cut)在3上,我们可以把3分为2个: [2 (3/3) 5]

因此LMax=3, RMin=3

偶数组: [1 4 7 9] 对应的中位数为 (4 + 7) /2 = 5.5,假定割(Cut)在4和7之间: [1 (4/7) 9]

因此LMax=4, RMin=7

割和第k个元素
一个数组
对于一个有序数组,对于数组A,如果在k的位置割(Cut)一下(不是割(Cut)在两数中间),那么 LMax = RMin = A[k],

两个数组
也就是我们题目的状态,我们要求得两个数组合并成一个有序数组时,第k位的元素

我们设:
Ci为第i个数组的割。

LMaxi为第i个数组割后的左元素.

RMini为第i个数组割后的右元素。

首先,LMax1<=RMin1,LMax2<=RMin2 这是肯定的,因为数组是有序的,左边肯定小于右边!,而如果割(Cut)在某个数上,则左右相等。

其次,如果我们让LMax1<=RMin2,LMax2<=RMin1 呢

那么如果左半边全小于右半边,如果左边的元素个数相加刚好等于k, 那么第k个元素就是Max(LMax1, LMax2),这个比较好理解的,因为Max(LMax1, LMax2)肯定是左边k个元素的最大值,因为合并后的数组是有序,第k个元素肯定前面k个元素中最大的那个。

那么如果 LMax1>RMin2,说明数组1的左边元素太大(多),我们把C1减小,C2=k-C1也就相应的增大。LMax2>RMin1同理,把C2减小,C1=k-C2也就相应的增大。

假设k=3

对于

[2 3 5]

[1 4 7 9]
设C1 = 1, 那么C2 = k - C1 = 2

[2 / 3 5]

[1 4 / 7 9]

这时LMax1 =2, RMin1 = 3, LMax2=4, RMin2=7,

从而有LMax2 > RMin1,依据前面的推论,我们要将C1增大,所以我们让C1 = 2,如下:

[2 3 /5]

[1 / 4 7 9]

这时LMax1 =3, RMin1 = 5, LMax2=1, RMin2=4, 满足 LMax1 < RMin2 且 LMax2 < RMin1 , 所以第3个元素为Max(LMax1,LMax2) = 3

两个数组的最大问题是,它们合并后,m+n总数可能为奇, 也可能为偶,所以我们得想法让m+n总是为偶数

通过虚拟加入‘#’,我们让m转换成2m+1 ,n转换成2n+1, 两数之和就变成了2m+2n+2,恒为偶数。

注意是虚拟加,其实根本没这一步,通过下面的转换,我们可以保证虚拟加后每个元素跟原来的元素一一对应

这么虚拟加后,每个位置可以通过/2得到原来元素的位置:

比如 2,原来在0位,现在是1位,1/2=0

比如 3,原来在1位,现在是3位,3/2=1

比如 5,原来在2位,现在是5位,5/2=2

比如 9,原来在3位,现在是7位,7/2=3

而对于割(Cut),如果割在‘#’上等于割在2个元素之间,割在数字上等于把数字划到2个部分,总是有以下成立:

LMaxi = (Ci-1)/2 位置上的元素
RMini = Ci/2 位置上的元素

例如:

割在3上,C = 3,LMax=a[(3-1)/2]=A[1],RMin=a[3/2] =A[1],刚好都是3的位置!

割在4/7之间‘#’,C = 4,LMax=A[(4-1)/2]=A[1]=4 ,RMin=A[4/2]=A[2]=7

剩下的事情就好办了,把2个数组看做一个虚拟的数组A,A有2m+2n+2个元素,割在m+n+1处,所以我们只需找到m+n+1位置的元素和m+n+2位置的元素就行了。(如3+4,16,割在8)

左边:A[m+n+1] = Max(LMax1,LMax2)

右边:A[m+n+2] = Min(RMin1,RMin2)

==>Mid = (A[m+n+1]+A[m+n+2])/2 = (Max(LMax1,LMax2) + Min(RMin1,RMin2) )/2

最快的割(Cut)是使用二分法,

有2个数组,我们对哪个做二分呢?
根据之前的分析,我们知道了,只要C1或C2确定,另外一个也就确定了。这里,为了效率,我们肯定是选长度较短的做二分,假设为C1。

LMax1>RMin2,把C1减小,C2增大。—> C1向左二分

LMax2>RMin1,把C1增大,C2减小。—> C1向右二分

如果C1或C2已经到头了怎么办?

这种情况出现在:如果有个数组完全小于或大于中值。假定n<m, 可能有4种情况:

C1 = 0 —— 数组1整体都在右边了,所以都比中值大,中值在数组2中,简单的说就是数组1割后的左边是空了,所以我们可以假定LMax1 = INT_MIN

C1 =2n —— 数组1整体都在左边了,所以都比中值小,中值在数组2中 ,简单的说就是数组1割后的右边是空了,所以我们可以假定RMin1= INT_MAX,来保证LMax2<RMin1恒成立

C2 = 0 —— 数组2整体在右边了,所以都比中值大,中值在数组1中 ,简单的说就是数组2割后的左边是空了,所以我们可以假定LMax2 = INT_MIN

C2 = 2m —— 数组2整体在左边了,所以都比中值小,中值在数组1中, 简单的说就是数组2割后的右边是空了,为了让LMax1 < RMin2 恒成立,我们可以假定RMin2 = INT_MAX

#include <stdio.h>
#include <vector>
using namespace std; #define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b)) class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size();
int m = nums2.size(); if (n > m) //保证数组1一定最短
{
return findMedianSortedArrays(nums2, nums1);
} // Ci 为第i个数组的割,比如C1为2时表示第1个数组只有2个元素。LMaxi为第i个数组割后的左元素。RMini为第i个数组割后的右元素。
int LMax1, LMax2, RMin1, RMin2, c1, c2, lo = 0, hi = 2 * n; //我们目前是虚拟加了'#'所以数组1是2*n长度,下标0-2n,(2N+1) while (lo <= hi) //二分
{
c1 = (lo + hi) / 2; //c1是二分的结果 在2n+1内部割一次,左边
c2 = m + n - c1; //2m+1内部割,割的数量为m+n+1-c1 //一共割m+n+1,(由于下标0-2m+2+1,故m+n-c1)不是在整体中2m+2m+2割,分别在两个2m+2和2n+2中割,加起来m+n+1
//想象两个数组割成四部分,前两部分合并为总数组的第一部分
LMax1 = (c1 == 0) ? INT_MIN : nums1[(c1 - 1) / 2];
RMin1 = (c1 == 2 * n) ? INT_MAX : nums1[c1 / 2];
LMax2 = (c2 == 0) ? INT_MIN : nums2[(c2 - 1) / 2];
RMin2 = (c2 == 2 * m) ? INT_MAX : nums2[c2 / 2]; if (LMax1 > RMin2)
hi = c1 - 1;
else if (LMax2 > RMin1)
lo = c1 + 1;
else
break;
}
return (max(LMax1, LMax2) + min(RMin1, RMin2)) / 2.0;
}
}; int main(int argc, char *argv[])
{
vector<int> nums1 = { 2,3, 5 };
vector<int> nums2 = { 1,4,7, 9 }; Solution solution;
double ret = solution.findMedianSortedArrays(nums1, nums2);
return 0;
}

作者:bian-bian-xiong
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/4-xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-shu/

my思路

/* 差不多的二分,但是由于没有加#处理导致细节处理麻烦。。。*/

这道题似乎限制了数组n,m均为int数组。

但是又没提,感觉怪怪的

假定两个有序数组长度分别为M,n(有序很重要)

题目要求时间复杂度为 O(log(m + n)),那么显然是m+n然后二分。关键在于如何二分。

想象两数组合并,最后的中位数必然左右各(M+N)/2个数。(先不考虑奇偶)

令较长的数组list1在前(这样中位数mid必然落在此数组数据范围内,否则mid<list1min或mid>list1max,左右两边数字数目不均等,不符合中位数定义。或者两数组恰好元素数量相等,中位数恰好在list1max与list2min之间)

设两数组为list1,list2,长度为m,n

将两个数组以下标cut1,cut2分别切开,左右分别为LMax1,RMin1,LMAX2, RMin2

LMAX1为list1[list1cut-1],RMIN1为list1[list1cut],LMAX2为list2[list2cut-1],RMIN2为list2[list2cut]

,(比如list[1,2,3,4],list1cut=2,LMAX1=2,RMIN1=3)

显然LMAX1<=RMIN1,LMAX2<=RMIN2

当满足LMAX1<=RMAX2,LMAX2<=RMIN1时,可求得中位数mid =(max(LMAX1,LMAX2)+min(RMIN1,RMIN2))/2

若LMAX1>RMAX2,  r = list1cut-1

若LMAX2>RMAX1,  l = list1cut+1

二分查找初始条件为l=0,r=M+N,

list1cut=(l+r)/2,

list2cut=(m+n)/2-list1cut;

这样list1贡献list1cut个数,lsit2贡献list2cut个数,相加共(m+n)/2个数,刚好为中位数左边的数。

现在考虑奇偶。

当m+n为偶数,(M+N)/2为int,按照如上所述处理即可。

其中LMAX1为list1[list1cut-1],RMIN1为list1[list1cut],LMAX2为list2[list2cut-1],RMIN2为list2[list2cut]

当m+n为奇数,中位数其实必然是m数组或n数组中某个数。二分时令list1和list2选出来的数个数加起来为(m+n+1)/2,当满足LMAX1<=RMIN2,LMAX2<=RMIN1时,mid=min(RMIN1,RMIN2)

初始化 r 为m+n

list1cut=(l+r)/2,

list2cut=(m+n)/2-list1cut;

然后上述思路的细节处理在于边界处理,比如listcut=0,或m,n这种情况时。处理方法为利用INT_MAX,MIN当不存在时规定。

然后跑了一次错了

例子为

[]
[1]

这才发现读题读错了。。。是不同时为空。加一个处理即可。

leetcode 4 寻找两个有序数组的中位数 二分法&INT_MAX的更多相关文章

  1. Java实现 LeetCode 4 寻找两个有序数组的中位数

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

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

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

  3. [LeetCode] 4. 寻找两个有序数组的中位数

    题目链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/ 题目描述: 给定两个大小为 m 和 n 的有序数组 nums1 和 ...

  4. 【LeetCode】寻找两个有序数组的中位数

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

  5. leetcode 4寻找两个有序数组的中位数

    最优解O(log(min(m,n))) /** 之前用合并有序数组的思想做了O((m+n+1)/2),现在试一试O(log(min(m,n))) 基本思路为:通过二分查找较小的数组得到对应的中位数(假 ...

  6. LeetCode Golang 4. 寻找两个有序数组的中位数

    4. 寻找两个有序数组的中位数 很明显我偷了懒, 没有给出正确的算法,因为官方的解法需要时间仔细看一下... func findMedianSortedArrays(nums1 []int, nums ...

  7. Leetcode(4)寻找两个有序数组的中位数

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

  8. 0004. 寻找两个有序数组的中位数(Java)

    4. 寻找两个有序数组的中位数 https://leetcode-cn.com/problems/median-of-two-sorted-arrays/ 最简单的就是用最简单的,把两个数组分别抽出然 ...

  9. leetcode题目4.寻找两个有序数组的中位数(困难)

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

随机推荐

  1. Linux服务器上迁移项目路径,修改nginx配置,迁移及备份MongoDB数据库流程 (超详细)!!!

    缘由:客户服务器项目路径不是很合理,导致Jenkins自动部署时还需要添加路径后再更新部署,所以需要把项目路径统一和规范化. 迁移项目路径,保证路径合规,同时做好备份和迁移.迁移后先安装好依赖. 项目 ...

  2. cts project的创建修改和删除

    事务码:SPRO_ADMIN进入 项目管理界面,点击工具栏创建项目(F5),弹出对话框,输入项目名称,回车确定. 标题中输入项目的描述.点击保存.如图: 点击图片放大 注:要想此项目在CTS建立请求的 ...

  3. springmvc 字符串转日期格式

    http://www.mamicode.com/info-detail-2485490.html

  4. springboot项目启动并立即执行自定义程序内容

    第一种:实现ApplicationRunner接口,重写其中的run()方法: 第二种:实现CommandLineRunner接口,重写其中的run()方法: 还有第三种...

  5. JS实现鼠标移入DIV随机变换颜色

    今天分享一个在 JavaScript中,实现一个鼠标移入可以随机变换颜色,本质就是js的随机数运用. 代码如下: <!DOCTYPE html> <html> <head ...

  6. 阿里云ECS hadoop+spark+zookeeper+hive code-server 集群搭建

    懒得重新排版然后发到博客了.用在线文档看吧 https://www.kdocs.cn/l/srV6o8rABW9V 用线上IDE(code-server)写scala的时候,出现BUG可以参考下面两篇 ...

  7. node集群(cluster)

    使用例子 为了让node应用能够在多核服务器中提高性能,node提供cluster API,用于创建多个工作进程,然后由这些工作进程并行处理请求. // master.js const cluster ...

  8. JAXB学习(一):概述

    pre.XML { background-color: rgba(255, 204, 204, 1); padding-left: 25px } JAXB是 Java Architecture for ...

  9. maven pom文件的 name 标签 和 url标签到底是什么作用

  10. vue3系列:vue3.0自定义虚拟滚动条V3Scroll|vue3模拟滚动条组件

    基于Vue3.0构建PC桌面端自定义美化滚动条组件V3Scroll. 前段时间有分享一个Vue3 PC网页端弹窗组件,今天带来最新开发的Vue3.0版虚拟滚动条组件. V3Scroll 使用vue3. ...