乘风破浪: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的更多相关文章

  1. 乘风破浪:LeetCode真题_033_Search in Rotated Sorted Array

    乘风破浪:LeetCode真题_033_Search in Rotated Sorted Array 一.前言     将传统的问题进行一些稍微的变形,这个时候我们可能无所适从了,因此还是实践出真知, ...

  2. 乘风破浪:LeetCode真题_026_Remove Duplicates from Sorted Array

    乘风破浪:LeetCode真题_026_Remove Duplicates from Sorted Array 一.前言     我们这次的实验是去除重复的有序数组元素,有大体两种算法. 二.Remo ...

  3. 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 ...

  4. 乘风破浪:LeetCode真题_034_Find First and Last Position of Element in Sorted Array

    乘风破浪:LeetCode真题_034_Find First and Last Position of Element in Sorted Array 一.前言 这次我们还是要改造二分搜索,但是想法却 ...

  5. 乘风破浪:LeetCode真题_023_Merge k Sorted Lists

    乘风破浪:LeetCode真题_023_Merge k Sorted Lists 一.前言 上次我们学过了合并两个链表,这次我们要合并N个链表要怎么做呢,最先想到的就是转换成2个链表合并的问题,然后解 ...

  6. 乘风破浪:LeetCode真题_021_Merge Two Sorted Lists

    乘风破浪:LeetCode真题_021_Merge Two Sorted Lists 一.前言 关于链表的合并操作我们是非常熟悉的了,下面我们再温故一下将两个有序链表合并成一个的过程,这是基本功. 二 ...

  7. 乘风破浪:LeetCode真题_041_First Missing Positive

    乘风破浪:LeetCode真题_041_First Missing Positive 一.前言 这次的题目之所以说是难,其实还是在于对于某些空间和时间的限制. 二.First Missing Posi ...

  8. 乘风破浪:LeetCode真题_040_Combination Sum II

    乘风破浪:LeetCode真题_040_Combination Sum II 一.前言 这次和上次的区别是元素不能重复使用了,这也简单,每一次去掉使用过的元素即可. 二.Combination Sum ...

  9. 乘风破浪:LeetCode真题_039_Combination Sum

    乘风破浪:LeetCode真题_039_Combination Sum 一.前言     这一道题又是集合上面的问题,可以重复使用数字,来求得几个数之和等于目标. 二.Combination Sum ...

随机推荐

  1. AngularJS国际化配置

    AngularJS国际化配置 下载angular-translate 下载zip包:https://github.com/angular-translate/bower-angular-transla ...

  2. javaweb 实现跨域

    现在的一个web应用会涉及到多个地方的restAPi的调用,传统的jsonp虽然支持跨域,但是只是支持get请求. 传统的ajax请求是不支持跨域的,是为了安全考虑. 跨域的思路是跟http机制有关, ...

  3. finally是否始终执行

    The only times finally won't be called are: If you call System.exit() If the JVM crashes first If th ...

  4. java并发编程(9)内存模型

    JAVA内存模型 在多线程这一系列中,不去探究内存模型的底层 一.什么是内存模型,为什么需要它 在现代多核处理器中,每个处理器都有自己的缓存,定期的与主内存进行协调: 想要确保每个处理器在任意时刻知道 ...

  5. hadoop学习笔记(二):简单启动

    一.hadoop组件依赖关系 二.hadoop日志格式: 两种日志,分别以out和log结尾: 1 以log结尾的日志:通过log4j日志记录格式进行记录的日志,采用日常滚动文件后缀策略来命名日志文件 ...

  6. 版本管控---Visual Source Safe(VSS)和Team Foundation Server(TFS)

    Visual Source Safe 是用于软件专业开发人员的源代码管理工具 Team Foundation Server是为开发团队提供集成源代码管理.问题跟踪和进程管理的更改管理系统 相同点 1. ...

  7. [编程] C语言结构体指针作为函数参数

    结构体指针作为函数参数:结构体变量名代表的是整个集合本身,作为函数参数时传递的整个集合,也就是所有成员,而不是像数组一样被编译器转换成一个指针.如果结构体成员较多,尤其是成员为数组时,传送的时间和空间 ...

  8. 解决:IDEA 中 new Java Class 怎么没了?

    前言:写代码时遇到的问题,所以记录下来.我的包名为“interface”,只有这个包及包下的文件不能建java文件. 问题 解决方式(对于普通包名) 点击应用,ok就可以了. 解决方式(对于包名为“i ...

  9. lincode 题目记录6

    the Duplicate Number  132 PatternFind 找重复的数字··直接暴力枚举是不行的···又超时提示·· 暴力枚举的写法· res=0 def findDuplicate( ...

  10. Linux上把新磁盘扩展到已有分区中(基于LVM)

    0.在不重启服务器的前提下找到新加的磁盘 --查看主机总线号 ls /sys/class/scsi_host/ --根据查询结果生成刷新磁盘语句 echo "- - -" > ...