题目
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3] nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2] nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
解答
这道题使用的是官方给出的解答,先看一些前瞻:
所以,这道题的要求是把两个数组 A, B 组合在一起并划分为两个半区Left 和 Right,这两个半区满足要求:
- Left.length = Right.length
- MaxLeft < MinRight
实现代码:(代码参照官方答案,加上自己理解的注释)
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int len1 = nums1.length;
int len2 = nums2.length;
if (len1 > len2) { //确保短的数组在前面
int[] temp = nums1;
nums1 = nums2;
nums2 = temp;
len1 = nums1.length;
len2 = nums2.length;
}
int iMin = 0;
int iMax = len1;
int halfLen = (len1 + len2 + 1) / 2;
while(iMax >= iMin) {
int i = (iMax + iMin) / 2;
int j = halfLen - i;
if (i < iMax && nums2[j-1] > nums1[i]) { //确定nums1[i]没有越界
//此时的i太小,需要右移
iMin = i + 1;
}
else if (i > iMin && nums1[i-1] > nums2[j] ) {
//此时的i太大了,需要左移
iMax = i - 1;
}
else {
//此时的i正好合适,处理特殊情况
int maxLeft = 0;
if (i == 0) { //第一个数组元素全在右半区
maxLeft = nums2[j-1];
}else if (j == 0) { //第一个数组元素全在左半区
maxLeft = nums1[i-1];
}
else {
maxLeft = Math.max(nums1[i-1], nums2[j-1]);
}
if((len1 + len2) % 2 == 1) {
//两数组总长度为奇数,中位数极为中间数
return maxLeft;
}
//两数组总长度为偶数,中位数为右半区最大和左半区最小的平均
int minRight = 0;
if (i == len1) {
minRight = nums2[j];
}else if (j == len2) {
minRight = nums1[i];
}else {
minRight = Math.min(nums1[i], nums2[j]);
}
return (minRight + maxLeft) / 2.0;
}
}
return 0.0;
}
}
结果
扩展
这道题难就难在数组的大小是不规定的,如果加上两个数组的长度一致这一条件,求中位数。会简单很多:
//长度一致的两个数组查找中位数,思想是一样的
public static double findMedian(int[] nums1, int[] nums2) {
while(nums1.length != 1) {
int l = nums1.length; //两个数组的长度是一致的
boolean mark = l % 2 == 0 ? true : false; //判断l的奇偶性
int index = mark ? l / 2 -1 : l / 2;
double med1 = mark ? (nums1[index] + nums1[index + 1]) / 2.0 : nums1[index];
double med2 = mark ? (nums2[index] + nums2[index + 1]) / 2.0 : nums2[index];
int[] temp1 = new int[index + 1];
int[] temp2 = new int[index + 1];
if (med1 < med2) {
//大的删去后面的,小的删去前面的
System.arraycopy(nums1, mark ? index + 1 : index, temp1, 0, index + 1);
System.arraycopy(nums2, 0, temp2, 0, index + 1);
}else {
System.arraycopy(nums2, mark ? index + 1 : index, temp2, 0, index + 1);
System.arraycopy(nums1, 0, temp1, 0, index + 1);
}
nums1 = temp1;
nums2 = temp2;
}
return (nums1[0] + nums2[0]) / 2.0;
}