乘风破浪: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. Java jstl标签使用总结

    1.在jsp文件中引用 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %&g ...

  2. js动画实现(一)

    requestAnimationFrame是什么? 在浏览器动画程序中,我们通常使用一个定时器来循环每隔几毫秒移动目标物体一次,来让它动起来.如今有一个好消息,浏览器开发商们决定:“嗨,为什么我们不在 ...

  3. HDU1596 find the safest road

    find the safest road XX星球有很多城市,每个城市之间有一条或多条飞行通道,但是并不是所有的路都是很安全的,每一条路有一个安全系数s,s是在 0 和 1 间的实数(包括0,1),一 ...

  4. Charles 抓取 iphone https的设置方式

    1. Charles:  help > SSL Proxying > Install Charles Root Certificate, 2. 将会打开 钥匙串访问 的功能,查找 Char ...

  5. SQL Serever学习14——存储过程和触发器

    存储过程 在数据库中很多查询都是大同小异,编写他们费时费力,将他们保存起来,以后执行就很方便了,把SQL语句“封装”起来. 存储过程的概念 存储过程是一组SQL语句集,经过编译存储,可以”一次编译,多 ...

  6. unity 和 iOS/Android 信息交互(方法调用)

    参考文章均来源于[大神雨松momo]的文章. unity -> iOS // unity 程序 usingSystem.Runtime.InteropServices; usingUnityEn ...

  7. DataSet常用简单方法

    Clear移除表中所有行来清除任何数据的DataSet Clone赋值该DataSet的结构但不复制数据 Copy赋值DataSet的结构和数据 Dispose释放DataSet对象 Equals确定 ...

  8. 文件下载(Servlet/Struts2)

    文件上传(Servlet/Struts2/SpringMVC)的链接:http://www.cnblogs.com/ghq120/p/8312944.html 文件下载 Servlet实现 目录结构 ...

  9. JDBC入门(3)--- PrepareStatement

    一.PrepareStatement概述 PrepareStatement是Statement接口的子接口: 1.强大之处: 防SQL攻击: 提高代码的可读性: 提高效率; 2.PrepareStat ...

  10. JSON运用——PHP中使用json数据格式定义字面量对象的方法

    目前,在PHP中是不支持字面量命名法. 前端的小伙伴都知道,在JS中用字面量定义一个对象的方法可以如下: var o = { 'name' : 'Tom' , 'url' : 'www.baidu.c ...