给定两个大小为 m 和 n 的正序(从小到大)数组 nums1
和 nums2
找出并返回这两个正序数组的中位数
时间复杂度O(log(m+n))
解法:二分法查找
public class Main {
public static void main(String[] args) {
Solution solution = new Solution();
int[] nums1 = {1,3,7,9};
int[] nums2 = {4,6,7};
System.out.println("mid "+solution.method(nums1, nums2));
}
}
class Solution {
public double method(int[] nums1, int[] nums2) {
int length1 = nums1.length;
int length2 = nums2.length;
/* 如果其中一个数组长度为0, 直接计算另有一个数组的中位数即可 */
if (length1 == 0) {
if (length2 %2 == 0) {
return (nums2[length2/2-1] + nums2[length2/2])/2.0;
}
else {
return nums2[length2/2];
}
}
else if (length2 == 0) {
if (length1 %2 == 0) {
return (nums1[length1/2-1] + nums1[length1/2])/2.0;
}
else {
return nums1[length1/2];
}
}
else {
/* 两个数组的长度都不为0的情况,使用二分查找 */
return binarySerchMid(nums1, nums2);
}
}
/*
假设两个数组的长度分别为m和n, 两数组归并之后, 数组A的[0,i)个元素位于中位数左侧, (i,m]的元素位于中位数右侧
数组B的[0,j)个元素位于中位数左侧, (j,n]的元素位于中位数右侧(A[i]和B[j]的相对位置还需进一步确定), 则有
(i+j) = (m+n+1)/2
其中m+n+1是为了解决m+n不是偶数的情况.
基于上述结论, 只要对其中一个数组使用二分查找出i, 即可根据j = (m+n+1)/2-i计算出j
假设中间位置左侧的最大值为MaxLeft, 右侧的最大值为MinRight
其中有2种情况需要特殊处理:
a. A所有元素都>=中位数, 此时i=0
b. B所有元素都>=中位数, 此时j=0
*/
private double binarySerchMid(int[] nums1,int[] nums2) {
int length1 = nums1.length;
int length2 = nums2.length;
int midLength = (length1+length2+1)/2;
/* 为了减少查找次数, 使用长度较小的数组进行二分查找
此处交换是为了使nums1始终是长度较小者
*/
if (length1 > length2) {
int[] temp = nums1;
nums1 = nums2;
nums2 = temp;
int tempLeng = length1;
length1 = length2;
length2 = tempLeng;
}
int midA = 0;
int midB = 0;
/* 二分法的左边界和右边界 */
int left = 0, right = length1;
/* 两数组按序合并后的中间两个数为maxLeft和minRight */
int maxLeft = 0, minRight = 0;
while (left <= right) {
midA = (left + right)/2;
midB = midLength - midA;
if ((midA < length1) && (nums1[midA] < nums2[midB-1])) {
/* midA小了(需防止越界, 下同) */
left = midA+1;
}
else if ((midA > 0) && (nums1[midA-1] > nums2[midB])) {
/* midA大了 */
right = midA-1;
}
else {
/* 二分法查找成功 */
break;
}
}
if (midA == 0) {
/* nums1全部>=中位数 */
maxLeft = nums2[midB-1];
}
else if (midB == 0) {
/* nums2全部>=中位数 */
maxLeft = nums1[midA-1];
}
else {
maxLeft = Math.max(nums1[midA-1], nums2[midB-1]);
}
if ((length1+length2)%2 != 0) {
/* 总长度为奇数, 中位数 = 中间位置元素 */
return maxLeft;
}
else {
/* 总长度为偶数, 中位数为中间两数的平均值 */
if (midA == length1) {
/* nums1整体<中位数 */
minRight = nums2[midB];
}
else if (midB == length2) {
/* nums2整体<中位数 */
minRight = nums1[midA];
}
else {
minRight = Math.min(nums1[midA], nums2[midB]);
}
return (maxLeft+minRight)/2.0;
}
}
}
输出:
6.0