给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
原题如上:
在看完官方讲解视频后,将代码及代码解析注释提炼出来,如下:
package t4;
public class zhongweishu {
public static void main(String[] args) {
int[] num1 = {7,9,11};
int[] num2 = {2,8,12};
//将长度短的设为第一个数组
if (num1.length > num2.length) {
int[] temp = num1;
num1 = num2;
num2 = temp;
}
//确定数组之后,再求数组长度
int l1 = num1.length;
int l2 = num2.length;
//分割线左边的元素个数等于右边或比右边多一
int total_left = (l1 + l2 + 1) / 2;
//i和j分别表示在数组1和数组2的分割线右边那个数的下标,那么下标数也是分割线左边数组的个数
//所以应该分割线应该满足num1[i-1]<=num2[j] && num2[j-1]<=num1[i]
int left = 0;//让左查找区间边界为0
int right = l1;//让右查找区间边界为短数组的长度
while (left < right) {//当 left>=right 时退出循环,求到极限的时候,分割线的位置就可以确定下来了
int i = left + (right - left + 1) / 2;//left和right中间位置的下标(取中位数),加一防止i-1的下标越界
int j = total_left - i;//剩下的就是j
//当分割线不满足要求时
if (num1[i-1] > num2[j]){//分割线太靠num1的右边了,分割线应该在i位置的左边
//下一个找分割线的区间应该是[left,i-1]这个区间
right = i - 1;//更改右边界
}else {
//搜素区间为上个区间取反,[i,right]
left = i;//更新左边界
}
}
//循环过后重新定义i和j
int i = left;
int j = total_left - i;
//分割线周围的四个元素可以确定下来了
//为防止下标越界,要做出一些调整
int n1_l_x = i == 0 ? Integer.MIN_VALUE : num1[i-1];
int n1_r_n = i == l1 ? Integer.MAX_VALUE : num1[i];
int n2_l_x = j == 0 ? Integer.MIN_VALUE : num2[j - 1];
int n2_r_n = j == l2 ? Integer.MAX_VALUE : num2[j];
if ((l1 + l2)%2 == 1) {//奇数个
int mid = Math.max(n1_l_x, n2_l_x);
System.out.println("中位数为:" + mid);
}else {//偶数个
double mid = (double) (Math.max(n1_l_x, n2_l_x)+Math.min(n1_r_n,n2_r_n)) /2;//注意强制转换
System.out.println("中位数为:" + mid);
}
}
}
运行结果如下: