乘风破浪:LeetCode真题_004_Median of Two Sorted Arrays
乘风破浪:LeetCode真题_004_Median of Two Sorted Arrays
一、前言
说到算法,最难的就是一些需要通过分析得到一些递推公式或者有用的结论,进而用来解决问题的方法了。这些东西一方面比较难以理解,另一方面都是有着严密的数学逻辑在里面的,并且需要通过程序来实现,无疑增加了求解的困难。就比如说这次的问题,在两个已经排好序的数组之中找到中间值。这里就涉及到两个数组总长度是奇数还是偶数,如何查找的问题了。
二、Median of Two Sorted Arrays
2.1 问题理解

2.2 分析解决
通过对题目的研究我们能够理解如果是奇数,则不用经过运算即可,如果是偶数,则需要将中间的两个数取平均值。最简单的想法就是将两个数组合并成一个数组,然后求中位数即可。但是这样的方式对于两个已经排好序的数组,需要遍历O(m+n)次,然后才能确定,其中m,n分别为两个已经排好序数组的长度。而题目要求算法时间复杂度为O(log2(m+n)),因此不能使用,那么我们要怎么做呢??
我们先来看看官方的算法:
首先进行分析,假设m<=n,两个数组A,B从小到大排列,那么如果我们分别对两个数组进行某种分割,分割的指针分别为i,j,并且分割之后要达到的结果是分成了四份,左边的两份A1,B1和在一起的长度等于右边A2,B2合在一起的长度,或者(+1),这样我们就找到了中间的几个数,但是我们还需要保证左边的所有元素的最大值要小于或者等于右边所有元素的最小值,如果能保证这两个条件,最终的结果就是左边所有的最大值与右边所有的最小值的平均值,或者左边/右边的最大值/最小值,具体看左边还是右边需要根据算法来考虑。这样就得到了解决问题的思路,可以使用二分查找算法来折半查找,同时也要注意一些边界的条件。





class Solution {
public double findMedianSortedArrays(int[] A, int[] B) {
int m = A.length;
int n = B.length;
if (m > n) { // to ensure m<=n
int[] temp = A; A = B; B = temp;
int tmp = m; m = n; n = tmp;
}
int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
while (iMin <= iMax) {
int i = (iMin + iMax) / 2;
int j = halfLen - i;
if (i < iMax && B[j-1] > A[i]){
iMin = i + 1; // i is too small
}
else if (i > iMin && A[i-1] > B[j]) {
iMax = i - 1; // i is too big
}
else { // i is perfect
int maxLeft = 0;
if (i == 0) { maxLeft = B[j-1]; }
else if (j == 0) { maxLeft = A[i-1]; }
else { maxLeft = Math.max(A[i-1], B[j-1]); }
if ( (m + n) % 2 == 1 ) { return maxLeft; }
int minRight = 0;
if (i == m) { minRight = B[j]; }
else if (j == n) { minRight = A[i]; }
else { minRight = Math.min(B[j], A[i]); }
return (maxLeft + minRight) / 2.0;
}
}
return 0.0;
}
}

下面来看看我们的思路:
public class Solution {
/**
* <pre>
* There are two sorted arrays nums1 and nums2 of size m and n respectively.
* Find the median of the two sorted arrays. The overall run time complexity
* should be O(log (m+n)).
*
* 题目大意:
* 两个排序数组,找这两个排序数组的中位数,时间复杂度为O(log(m+n))
*
* 题解思路:
* 递归分治求解
* </pre>
*
* @param nums1
* @param nums2
* @return
*/
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if (nums1 == null) {
nums1 = new int[0];
}
if (nums2 == null) {
nums2 = new int[0];
}
int len1 = nums1.length;
int len2 = nums2.length;
if (len1 < len2) {
// 确保第一个数组比第二个数组长度大
return findMedianSortedArrays(nums2, nums1);
}
// 如果长度小的数组长度为0,就返回前一个数组的中位数
if (len2 == 0) {
return (nums1[(len1 - 1) / 2] + nums1[len1 / 2]) / 2.0;
}
int lo = 0;
int hi = len2 * 2;
int mid1;
int mid2;
double l1;
double l2;
double r1;
double r2;
while (lo <= hi) {
mid2 = (lo + hi) / 2;
mid1 = len1 + len2 - mid2;
l1 = (mid1 == 0) ? Integer.MIN_VALUE : nums1[(mid1 - 1) / 2];
l2 = (mid2 == 0) ? Integer.MIN_VALUE : nums2[(mid2 - 1) / 2];
r1 = (mid1 == len1 * 2) ? Integer.MAX_VALUE : nums1[mid1 / 2];
r2 = (mid2 == len2 * 2) ? Integer.MAX_VALUE : nums2[mid2 / 2];
if (l1 > r2) {
lo = mid2 + 1;
} else if (l2 > r1) {
hi = mid2 - 1;
} else {
return (Math.max(l1, l2) + Math.min(r1, r2)) / 2;
}
}
return -1;
}
}
注意我们这里的算法和官方的原理一样,只不过是将分割的坐标都扩大了两倍,这样更加能优化算法的性能,并且因为扩大了2倍之后,其他地方也做了相应的改变而已。

三、总结
遇到这样的问题,最重要的是要学会如何去建模,然后进行解决,涉及到了一定的算法和逻辑思维能力,对分析和理解都有很大的考察价值。
乘风破浪:LeetCode真题_004_Median of Two Sorted Arrays的更多相关文章
- 乘风破浪:LeetCode真题_033_Search in Rotated Sorted Array
乘风破浪:LeetCode真题_033_Search in Rotated Sorted Array 一.前言 将传统的问题进行一些稍微的变形,这个时候我们可能无所适从了,因此还是实践出真知, ...
- 乘风破浪:LeetCode真题_026_Remove Duplicates from Sorted Array
乘风破浪:LeetCode真题_026_Remove Duplicates from Sorted Array 一.前言 我们这次的实验是去除重复的有序数组元素,有大体两种算法. 二.Remo ...
- leetcode第二题--Median of Two Sorted Arrays
Problem:There are two sorted arrays A and B of size m and n respectively. Find the median of the two ...
- 乘风破浪:LeetCode真题_034_Find First and Last Position of Element in Sorted Array
乘风破浪:LeetCode真题_034_Find First and Last Position of Element in Sorted Array 一.前言 这次我们还是要改造二分搜索,但是想法却 ...
- 乘风破浪:LeetCode真题_023_Merge k Sorted Lists
乘风破浪:LeetCode真题_023_Merge k Sorted Lists 一.前言 上次我们学过了合并两个链表,这次我们要合并N个链表要怎么做呢,最先想到的就是转换成2个链表合并的问题,然后解 ...
- 乘风破浪:LeetCode真题_021_Merge Two Sorted Lists
乘风破浪:LeetCode真题_021_Merge Two Sorted Lists 一.前言 关于链表的合并操作我们是非常熟悉的了,下面我们再温故一下将两个有序链表合并成一个的过程,这是基本功. 二 ...
- 乘风破浪:LeetCode真题_041_First Missing Positive
乘风破浪:LeetCode真题_041_First Missing Positive 一.前言 这次的题目之所以说是难,其实还是在于对于某些空间和时间的限制. 二.First Missing Posi ...
- 乘风破浪:LeetCode真题_040_Combination Sum II
乘风破浪:LeetCode真题_040_Combination Sum II 一.前言 这次和上次的区别是元素不能重复使用了,这也简单,每一次去掉使用过的元素即可. 二.Combination Sum ...
- 乘风破浪:LeetCode真题_039_Combination Sum
乘风破浪:LeetCode真题_039_Combination Sum 一.前言 这一道题又是集合上面的问题,可以重复使用数字,来求得几个数之和等于目标. 二.Combination Sum ...
随机推荐
- Linux下设置Tomcat开机启动
1.进入/etc/rc.d/init.d,新建文件tomcat,并让它成为可执行文件 chmod 755 tomcat. #!/bin/bash # # /etc/rc.d/init.d/tomcat ...
- PHP之string之str_pad()函数使用
str_pad (PHP 4 >= 4.0.1, PHP 5, PHP 7) str_pad - Pad a string to a certain length with another st ...
- 转载:BIO | NIO | AIO
http://my.oschina.net/bluesky0leon/blog/132361 也谈BIO | NIO | AIO (Java版) 转载自:zheng-lee博客 发布时间: 201 ...
- HDFS常用shell命令
注,其实常用命令不用网上搜,和linux下的命令很类似,触类旁通,直接在linux 上 hadoop fs 看一下就行了,不需要刻意去记我把 linux 上的 help 列举下,方便直接看吧,hdfs ...
- Centos7环境下 安装ffmage2.7.1过程
参考http://trac.ffmpeg.org/wiki/CompilationGuide/Centos#FFmpeg 先查看了官方文档,消化以后,开始自己编译ffmage2.7.1,所需要安装包, ...
- c#创建window服务
Windows服务在Visual Studio 以前的版本中叫NT服务,在VS.net启用了新的名称.用Visual C# 创建Windows服务不是一件困难的事,本文就将指导你一步一步创建一个Win ...
- ubuntu16.04 安装 nginx 服务器
在线安装 apt-get install nginx 说明 启动程序文件在/usr/sbin/nginx 日志放在了/var/log/nginx中,分别是access.log和error.log 并已 ...
- javascript window.opener的用法分析
window.opener 返回的是创建当前窗口的那个窗口的引用 window.opener 的用法 window.opener 返回的是创建当前窗口的那个窗口的引用,比如点击了a.htm上的一个链接 ...
- 设计模式之——外观or门面模式
1.概念 定义一个高层的统一的外观接口类,该接口用于客户端调用,和一个实现类用来包装子系统中多个类,客户端可以通过客户端完成对子系统的方法调用. 2.适用场景 2.1 代码移植,降低了现有系统的复杂度 ...
- 关于springmvc中常用的注解,自己也整理一下
1.@Controller 在springMVC中@controller主要用在控制层的类上,之前只知道用注解开发的时候必须加一个@controller ,今天看了别的大佬整理的才知道为什么这么用,控 ...