问题:

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

方法一:

  1. 将两个有序数组,合并成一个有序数组,取中位数。
  2. 创建一个新的数组,长度为m+n。
  3. 用两个指针记录数组当前位置的索引,每次比较这两个的值,将较小的那个值加入新数组,这个指针后移。
  4. 每次循环开始时,有限判断是否有指针跑到底,之后只迁移另一个数组的值。
  5. 时间复杂度是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

方法二:

  1. 从方法一中可以看出,至少做了一半的无谓操作。当指针跑到中位数的时候,其实可以直接计算出来,然后break。甚至于,不需要额外的空间去纪录新的数组。
  2. 定义2个目标索引,记录中位数的位置,这里可以实现奇偶情况统一,分别是(LENGTH-1)/2和LENGTH/2,在指针跑到LENGTH/2的时候break,将累加的值除以2就是中位数的值。
  3. 定义两个引用,当前操作数组的引用currentArray和当前数组的索引currentIndex,之后两个指针的自增和currentIndex没有关系,用这两个引用去操作中位数的累加赋值。
  4. 同样需要考虑,在指针跑到一个数组结束之后,仍然没有跑到中位数相关数字的情况,这时候,可以在剩余数组中直接计算中位数的值。
  5. 有一种最特殊的情况,总长度是偶数,一个数组跑完之后,记录了中位数的第一个值,针对这种情况,使用一个标志位,在第一次中位数赋值之后改为true,然后与剩余数组的第一个值相加除以2.0就是中位数的值。
  6. 时间复杂度是方法一的一半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

方法三:

  1. 可以借鉴二分查找法的思想:比较两个数组的中间值,去掉中间值较小数组的前面部分,和中间值较大数组的后面部分,形成两个新的数组进行递归。
  2. 这种方法需要考虑很多特殊情况,极易出错,但是确实具有O(log(m+n))的时间复杂度。
  3. 首先每一次两个数组去掉的部分,长度必须相同,保证最终的中位数不会改变,如果长度不一,去较小的那个值去掉。
  4. 当一个数组的长度为0时,直接计算另一个数组的中位数。
  5. 在保证两个数组的长度均不为0,这意味着,两个数组都有了“中间值”这个概念。考虑两个数组的中间值相等的情况,根据两个数组长度的奇偶性,又可以分成三种情况讨论。
  6. 在中间值相等的情况下,两个数组长度均为奇数,中间值即是最后的中位数。
  7. 在中间值相等的情况下,两个数组长度均为偶数,中位数是中间值与两数组中间值之后的那一个数的较小值的平均值。
  8. 在中间值相等的情况下,两个数组长度一奇一偶,中位数就是偶数长度数组的中位数。
  9. 考虑一下,每次去掉的部分,是不是会影响最终的值?有一种特殊的情况会造成最终的中位数值变化,要特殊讨论。就是:两个偶数长度的数组,中间值较大的数组和它之后第一个值完全被另一个数组从两侧包裹,如:[2,3]和[0,1,4,5]。2>1且3<4,按照正常的流程,下一次递归的数组应该是[2]和[1,4,5],很明显结果被改变了。这种情况下直接取[2,3]数组的中位数作为最终结果即可。
  10. 如果去掉的长度为0怎么办?这时候,要考虑一下当一个数组的长度为1时,该怎么处理。首先考虑,另一个数组的长度也为1,这时候直接取平均值。
  11. 一般来说,理想的情况是,将这个长度为1的数组消掉,进入下一次循环,但是这必须是这个值并不会影响到最终中位数计算的前提下。根据另一个数组长度的奇偶性分2种情况考虑。
  12. 另一个数组长度为偶数,长度为1的数组会影响到最后结果的情况是:这个值大于另一个数组的中间值,却小于另一个数组中间值之后的那个值,典型的就是[2]和[0,1,3,4]。
  13. 另一个数组长度为奇数,长度为1的数组会影响到最后结果的情况是:这个值大于另一个数组的中间值,却小于之后的值,如[4]和[1,3,5]。或者这个值小于另一个数组的中间值,却大于之前的值,如[2]和[1,3,5]。
  14. 在没有出现类似12和13的情况下,可以没有顾忌的削去这个数组,同时另一个数组根据中间值比较的情况,选择去掉数组的第一个值还是最后一个值,进入下一次递归。
  15. 除了存在长度为1的数组,还有1种可能导致削去长度为0的情况,就是中价值较小的数组长度为2。这种情况下,中间值较小的数组去掉第一项,另一个数组去掉最后一项,进入下一次递归。
  16. 最后,记得入参检查,存在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/

https://github.com/Gerrard-Feng/LeetCode/blob/master/LeetCode/src/com/gerrard/algorithm/hard/Q004.java

PS:如有不正确或提高效率的方法,欢迎留言,谢谢!

No.004:Median of Two Sorted Arrays的更多相关文章

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

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

  3. Q4:Median of Two Sorted Arrays

    4. Median of Two Sorted Arrays 官方的链接:4. Median of Two Sorted Arrays Description : There are two sort ...

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

  5. 【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 ...

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

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

  8. leetcode 4:Median of Two Sorted Arrays

    public double FindMedianSortedArrays(int[] nums1, int[] nums2) { int t=nums1.Length+nums2.Length; in ...

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

随机推荐

  1. Win10命令提示符(cmd)怎么复制粘贴

    在Win10系统里右键开始菜单,选择弹出菜单里的命令提示符,如下图所示: 然后复制要粘贴的文字,例如: echo hovertree.com 把上面的文字复制后,点击命令提示符窗口,然后在命令提示符窗 ...

  2. BZOJ 1010: [HNOI2008]玩具装箱toy [DP 斜率优化]

    1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 9812  Solved: 3978[Submit][St ...

  3. http协议(十一)http与https

    一.http的缺点 之前有介绍过http协议相关的一些知识,http是相当优秀和方便的,但它也有缺点,主要不足表现在如下几个方面: △ 通信使用明文(不加密),内容可能会被窃听 △ 不验证通信方的身份 ...

  4. Linux.NET实战手记—自己动手改泥鳅(上)

    各位读者大家好,不知各位读者有否阅读在下的前一个系列<Linux.NET 学习手记>,在前一个系列中,我们从Linux中Mono的编译安装开始,到Jexus服务器的介绍,以及如何在Linu ...

  5. 【腾讯Bugly干货分享】H5 视频直播那些事

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57a42ee6503dfcb22007ede8 Dev Club 是一个交流移动 ...

  6. 【Java并发编程实战】-----“J.U.C”:CountDownlatch

    上篇博文([Java并发编程实战]-----"J.U.C":CyclicBarrier)LZ介绍了CyclicBarrier.CyclicBarrier所描述的是"允许一 ...

  7. 免费的精品: Productivity Power Tools 动画演示

    Productivity Power Tools 是微软官方推出的 Visual Studio 扩展,被用以提高开发人员生产率.它的出现一定程度上弥补和完善了 Visual Studio 自身的不足, ...

  8. AWS开发人员认证考试样题解析

    最近在准备AWS的开发人员考试认证.所以特意做了一下考试样题.每道题尽量给出了文档出处以及解析. Which of the following statements about SQS is true ...

  9. 海量数据处理利器greenplum——初识

    简介及适用场景 如果想在数据仓库中快速查询结果,可以使用greenplum. Greenplum数据库也简称GPDB.它拥有丰富的特性: 第一,完善的标准支持:GPDB完全支持ANSI SQL 200 ...

  10. Entity Framework 6 Recipes 2nd Edition(10-3)译 -> 返回结果是一个标量值

    10-3. 返回结果是一个标量值 问题 想取得存储过程返回的一个标量值. 解决方案 假设我们有如Figure 10-2所示的ATM机和ATM机取款记录的模型 Figure 10-2. 一个ATM机和A ...