No.004:Median of Two Sorted Arrays
问题:
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)).
Example1:
nums1 = [1, 3]
nums2 = [2]
The median is 2.0
Example2:
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5
官方难度:
Hard
翻译:
两个已排序数组,其长度分别是m和n,找出这两个数组的中位数。
要求整个算法的时间复杂度是O(log(m+n))。
例子:
[1,3]和[2],中位数是2.0
[1,2]和[3,4],中位数是2.5
方法一:
- 将两个有序数组,合并成一个有序数组,取中位数。
- 创建一个新的数组,长度为m+n。
- 用两个指针记录数组当前位置的索引,每次比较这两个的值,将较小的那个值加入新数组,这个指针后移。
- 每次循环开始时,有限判断是否有指针跑到底,之后只迁移另一个数组的值。
- 时间复杂度是O(m+n)。
方法一的解题代码:
// 将两个数组合并成新数组取中位数,时间复杂度是O(m+n)
private static double method1(int[] array1, int[] array2) {
final int LENGTH = array1.length + array2.length;
int[] resultArray = new int[LENGTH];
// 数组当前位置索引
int index1 = 0;
int index2 = 0;
int[] tempArray = null;
int tempIndex = 0;
// 合并数组赋值
for (int i = 0; i < LENGTH; i++) {
if (tempArray == null) {
// 优先判断其中一个数组已经被掏空的情况
// 特别注意,掏空的时候index会比最后的index+1
if (index1 == array1.length || index2 == array2.length) {
// temp赋值+本次赋值
if (index1 == array1.length) {
tempArray = array2;
tempIndex = index2;
resultArray[i] = tempArray[tempIndex++];
} else {
tempArray = array1;
tempIndex = index1;
resultArray[i] = tempArray[tempIndex++];
}
// 这个continue是必要的,赋值之后,直接下一次循环
continue;
}
// 两个数组都有剩余的情况
if (array1[index1] < array2[index2]) {
resultArray[i] = array1[index1++];
} else {
resultArray[i] = array2[index2++];
}
} else {
// 操作tempArray
resultArray[i] = tempArray[tempIndex++];
}
}
// 分奇偶确定中位数,除数要特别标注2.0,不然以整数规则计算
if (LENGTH % 2 == 0) {
return (resultArray[LENGTH / 2] + resultArray[LENGTH / 2 - 1]) / 2.0;
}
return resultArray[(LENGTH - 1) / 2];
}
method1
方法二:
- 从方法一中可以看出,至少做了一半的无谓操作。当指针跑到中位数的时候,其实可以直接计算出来,然后break。甚至于,不需要额外的空间去纪录新的数组。
- 定义2个目标索引,记录中位数的位置,这里可以实现奇偶情况统一,分别是(LENGTH-1)/2和LENGTH/2,在指针跑到LENGTH/2的时候break,将累加的值除以2就是中位数的值。
- 定义两个引用,当前操作数组的引用currentArray和当前数组的索引currentIndex,之后两个指针的自增和currentIndex没有关系,用这两个引用去操作中位数的累加赋值。
- 同样需要考虑,在指针跑到一个数组结束之后,仍然没有跑到中位数相关数字的情况,这时候,可以在剩余数组中直接计算中位数的值。
- 有一种最特殊的情况,总长度是偶数,一个数组跑完之后,记录了中位数的第一个值,针对这种情况,使用一个标志位,在第一次中位数赋值之后改为true,然后与剩余数组的第一个值相加除以2.0就是中位数的值。
- 时间复杂度是方法一的一半O((m+n)/2),仍然未达到要求。
方法二的解题代码:
// 不需要重组整个数组,只需要重组一半,时间复杂度是O((m+n)/2)
private static double method2(int[] array1, int[] array2) {
final int LENGTH = array1.length + array2.length;
// 中位数的索引
int targetIndex1 = (LENGTH - 1) / 2;
int targetIndex2 = LENGTH / 2;
// 结果
int result = 0;
boolean flag = false;
// 数组当前位置索引
int index1 = 0;
int index2 = 0;
// 当前操作数组及索引
int[] currentArray;
int currentIndex;
// 开启循环
for (int i = 0; i < LENGTH; i++) {
// 某一数组遍历结束
if (index1 == array1.length || index2 == array2.length) {
int[] remain, exhaust;
if (index1 == array1.length) {
remain = array2;
exhaust = array1;
} else {
remain = array1;
exhaust = array2;
}
if (!flag) {
// 中位数在之后,可以直接计算
return (remain[(LENGTH - 1) / 2 - exhaust.length] + remain[LENGTH / 2 - exhaust.length]) / 2.0;
} else {
// 偶数位的总长度,且result已经加过一次
return (result + remain[i - exhaust.length]) / 2.0;
}
}
// 索引自增,当前操作数组及索引赋值
if (array1[index1] < array2[index2]) {
currentArray = array1;
currentIndex = index1;
index1++;
} else {
currentArray = array2;
currentIndex = index2;
index2++;
}
// 结果赋值,奇偶长度统一
if (i == targetIndex1) {
flag = true;
result += currentArray[currentIndex];
}
if (i == targetIndex2) {
result += currentArray[currentIndex];
break;
}
}
return result / 2.0;
}
method2
方法三:
- 可以借鉴二分查找法的思想:比较两个数组的中间值,去掉中间值较小数组的前面部分,和中间值较大数组的后面部分,形成两个新的数组进行递归。
- 这种方法需要考虑很多特殊情况,极易出错,但是确实具有O(log(m+n))的时间复杂度。
- 首先每一次两个数组去掉的部分,长度必须相同,保证最终的中位数不会改变,如果长度不一,去较小的那个值去掉。
- 当一个数组的长度为0时,直接计算另一个数组的中位数。
- 在保证两个数组的长度均不为0,这意味着,两个数组都有了“中间值”这个概念。考虑两个数组的中间值相等的情况,根据两个数组长度的奇偶性,又可以分成三种情况讨论。
- 在中间值相等的情况下,两个数组长度均为奇数,中间值即是最后的中位数。
- 在中间值相等的情况下,两个数组长度均为偶数,中位数是中间值与两数组中间值之后的那一个数的较小值的平均值。
- 在中间值相等的情况下,两个数组长度一奇一偶,中位数就是偶数长度数组的中位数。
- 考虑一下,每次去掉的部分,是不是会影响最终的值?有一种特殊的情况会造成最终的中位数值变化,要特殊讨论。就是:两个偶数长度的数组,中间值较大的数组和它之后第一个值完全被另一个数组从两侧包裹,如:[2,3]和[0,1,4,5]。2>1且3<4,按照正常的流程,下一次递归的数组应该是[2]和[1,4,5],很明显结果被改变了。这种情况下直接取[2,3]数组的中位数作为最终结果即可。
- 如果去掉的长度为0怎么办?这时候,要考虑一下当一个数组的长度为1时,该怎么处理。首先考虑,另一个数组的长度也为1,这时候直接取平均值。
- 一般来说,理想的情况是,将这个长度为1的数组消掉,进入下一次循环,但是这必须是这个值并不会影响到最终中位数计算的前提下。根据另一个数组长度的奇偶性分2种情况考虑。
- 另一个数组长度为偶数,长度为1的数组会影响到最后结果的情况是:这个值大于另一个数组的中间值,却小于另一个数组中间值之后的那个值,典型的就是[2]和[0,1,3,4]。
- 另一个数组长度为奇数,长度为1的数组会影响到最后结果的情况是:这个值大于另一个数组的中间值,却小于之后的值,如[4]和[1,3,5]。或者这个值小于另一个数组的中间值,却大于之前的值,如[2]和[1,3,5]。
- 在没有出现类似12和13的情况下,可以没有顾忌的削去这个数组,同时另一个数组根据中间值比较的情况,选择去掉数组的第一个值还是最后一个值,进入下一次递归。
- 除了存在长度为1的数组,还有1种可能导致削去长度为0的情况,就是中价值较小的数组长度为2。这种情况下,中间值较小的数组去掉第一项,另一个数组去掉最后一项,进入下一次递归。
- 最后,记得入参检查,存在null的输入或两个数组长度和为0时,抛出异常。
方法三的解题代码:
// 因为两数组分别有序,用类二分查找法寻找中位数,时间复杂度O(log(m+n))
public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
// 入参校验
if (nums1 == null || nums2 == null || nums1.length + nums2.length == 0) {
throw new IllegalArgumentException("Input error");
}
// 特殊情况:一个数组长度为0
if (nums1.length == 0 || nums2.length == 0) {
int[] array = nums1.length == 0 ? nums2 : nums1;
return (array[array.length / 2] + array[(array.length - 1) / 2]) / 2.0;
}
// 中位数
int median1 = nums1[(nums1.length - 1) / 2];
int median2 = nums2[(nums2.length - 1) / 2];
// 特殊情况:两个数组的中位数相同
if (median1 == median2) {
// 两数组长度为奇数
if (nums1.length % 2 == 1 && nums2.length % 2 == 1) {
return median1;
}
// 两数组长度为偶数
if (nums1.length % 2 == 0 && nums2.length % 2 == 0) {
return (median1 + Math.min(nums1[nums1.length / 2], nums2[nums2.length / 2])) / 2.0;
}
// 一奇一偶,就是偶数长度数组的中位数
int[] array = nums1.length % 2 == 0 ? nums1 : nums2;
return (median1 + array[array.length / 2]) / 2.0;
}
// 特殊情况:一个数组长度为1
if (nums1.length == 1 || nums2.length == 1) {
int[] array1, array2;
if (nums1.length == 1) {
array1 = nums1;
array2 = nums2;
} else {
array1 = nums2;
array2 = nums1;
}
// 特殊情况:另一个数组的长度也为1
if (array2.length == 1) {
return (array1[0] + array2[0]) / 2.0;
}
// 特殊情况:两个数组的中位数计算,包含长度为1的数组的值
if (array2.length % 2 == 0) {
if (array1[0] >= array2[(array2.length - 1) / 2] && array1[0] <= array2[array2.length / 2]) {
return array1[0];
}
} else {
if (array1[0] < array2[array2.length / 2] && array1[0] > array2[array2.length / 2 - 1]) {
return (array1[0] + array2[array2.length / 2]) / 2.0;
}
if (array1[0] > array2[array2.length / 2] && array1[0] < array2[array2.length / 2 + 1]) {
return (array1[0] + array2[array2.length / 2]) / 2.0;
}
}
// 将array1长度降为0,进入下一次递归
if (array1[0] > array2[array2.length / 2]) {
return findMedianSortedArrays(new int[0], Arrays.copyOfRange(array2, 1, array2.length));
} else {
return findMedianSortedArrays(new int[0], Arrays.copyOf(array2, array2.length - 1));
}
}
// 中位数大的数组是array1
int[] array1, array2;
if (median1 > median2) {
array1 = nums1;
array2 = nums2;
} else {
array1 = nums2;
array2 = nums1;
}
// 特殊情况:两个数组的中位数,就是array1的中位数
// 只有在两个偶数长度的数组的情况下,才能实现
if (array1.length % 2 == 0 && array2.length % 2 == 0) {
if (array1[array1.length / 2] <= array2[array2.length / 2]) {
return (array1[(array1.length - 1) / 2] + array1[array1.length / 2]) / 2.0;
}
}
int reduce1 = array1.length / 2;
int reduce2 = (array2.length - 1) / 2;
// 特殊情况:array2的长度为2
if (reduce2 == 0) {
return findMedianSortedArrays(Arrays.copyOf(array1, array1.length - 1), new int[] { array2[1] });
}
int reduce = Math.min(reduce1, reduce2);
// 削去数组递归
return findMedianSortedArrays(Arrays.copyOf(array1, array1.length - reduce),
Arrays.copyOfRange(array2, reduce, array2.length));
}
findMedianSortedArrays
相关链接:
https://leetcode.com/problems/median-of-two-sorted-arrays/
PS:如有不正确或提高效率的方法,欢迎留言,谢谢!
No.004:Median of Two Sorted Arrays的更多相关文章
- LeetCode2:Median of Two Sorted Arrays
题目: There are two sorted arrays A and B of size m and n respectively. Find the median of the two sor ...
- leetcode第四题:Median of Two Sorted Arrays (java)
Median of Two Sorted Arrays There are two sorted arrays A and B of size m and n respectively. Find t ...
- Q4:Median of Two Sorted Arrays
4. Median of Two Sorted Arrays 官方的链接:4. Median of Two Sorted Arrays Description : There are two sort ...
- LeetCode第[4]题(Java):Median of Two Sorted Arrays 标签:Array
题目难度:hard There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median ...
- 【LeetCode算法题库】Day2:Median of Two Sorted Arrays & Longest Palindromic Substring & ZigZag Conversion
[Q4] There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of th ...
- 4:Median of Two Sorted Arrays
here are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two ...
- LeetCode第[4]题(Java):Median of Two Sorted Arrays (俩已排序数组求中位数)——HARD
题目难度:hard There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median ...
- leetcode 4:Median of Two Sorted Arrays
public double FindMedianSortedArrays(int[] nums1, int[] nums2) { int t=nums1.Length+nums2.Length; in ...
- LeetCode 004 Median of Two Sorted Arrays
题目描述:Median of Two Sorted Arrays There are two sorted arrays A and B of size m and n respectively. F ...
随机推荐
- MySQL优化聊两句
原文地址:http://www.cnblogs.com/verrion/p/mysql_optimised.html MySQL优化聊两句 MySQL不多介绍,今天聊两句该如何优化以及从哪些方面入手, ...
- Postman - 功能强大的 API 接口请求调试和管理工具
Postman 是一款功能强大的的 Chrome 应用,可以便捷的调试接口.前端开发人员在开发或者调试 Web 程序的时候是需要一些方法来跟踪网页请求的,用户可以使用一些网络的监视工具比如著名的 Fi ...
- Android开发学习—— Fragment
#Fragment* 用途:在一个Activity里切换界面,切换界面时只切换Fragment里面的内容* 生命周期方法跟Activity一致,可以理解把其为就是一个Activity* 定义布局文件作 ...
- 简述我的SOA服务治理
SOA服务治理 1.解决业务部门服务冲突和纠纷2.版本定义与版本管理3.服务备案与服务管理4.业务监督与服务监控 SOA的战略目的 一.业务价值胜过技术策略 二.战略目标胜过具体项目的效益 三.内置的 ...
- 您真的理解了SQLSERVER的日志链了吗?
您真的理解了SQLSERVER的日志链了吗? 先感谢宋沄剑给本人指点迷津,还有郭忠辉童鞋今天在QQ群里抛出的问题 这个问题跟宋沄剑讨论了三天,再次感谢宋沄剑 一直以来,SQLSERVER提供了一个非常 ...
- EasyPR--开发详解(6)SVM开发详解
在前面的几篇文章中,我们介绍了EasyPR中车牌定位模块的相关内容.本文开始分析车牌定位模块后续步骤的车牌判断模块.车牌判断模块是EasyPR中的基于机器学习模型的一个模块,这个模型就是作者前文中从机 ...
- 如何在VMware中安装Windows Phone SDK 8.0 (支持模拟器调试)
相信很多开发者目前的系统还是Win7或Mac,一般不会为了开发某个程序而重装系统,所以我们就需要用到VMware这类的虚拟机来模拟预期的开发环境.在开始介绍前,给大家说明下我当前的软硬件环境,本文所讲 ...
- 简化SSH框架的整合
一.开发环境: (1) OS:Windows 7 (2) DB:MySql 5.1.6 (3) JDK:1.8.0_17 (4) Server:Apache Tomcat 8. ...
- Electron中Jquery的引入方式
原文链接http://huisky.com/blog/16122220522957 Electron默认启用了Node.js的require模块,而jQuery等新版本框架为了支持commondJS标 ...
- php排序算法
<?php//冒泡排序(数组排序) function bubble_sort($array){ $count = count($array); if ($count <= 0) retur ...