思路1:将两个数组按照索引i与索引j切分为左右两个部分,左半部分与右半部分的元素数量相同,并且寻找到左半部分最大值小于右半部分最小值的位置,中位数就在附近,具体的根据总长度是偶数/奇数确定。寻找索引的过程为二分。时间复杂度为O(logmin(shortLength,longLength))
思路2:转换为寻找第k小的数字,相当于对两个数组分别寻找第k/2小的数字,由于两个数组都是有序的,所以可以直接根据下标对不可能是第k/2小的数字进行删除,该操作的时间复杂度为O(1)。由于是二分删除,所以时间复杂度为O(log((m+n)/2)),即O(log(m+n))。
以下是思路一的代码:
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if(nums1==null||nums2==null||(nums1.length==0&&nums2.length==0)) return 0.0;
//如果一个数组为空,就变成了寻找单个有序数组的中位数问题,单独写了一个简单的方法
if(nums1.length==0) return getMiddleNum(nums2,0,nums2.length-1);
if(nums2.length==0) return getMiddleNum(nums1,0,nums1.length-1);
int totalLength=nums1.length+nums2.length;
//设置较长数组与较短数组及其长度
int[] shortArray,longArray;
int shortLength,longLength;
if(nums1.length<nums2.length){
shortArray=nums1;
longArray=nums2;
}
else{
shortArray=nums2;
longArray=nums1;
}
shortLength=shortArray.length;
longLength=longArray.length;
//搜索边界为[-1,短数组的最后一个元素],索引为i时,隔板放在第i个元素之后
int left=-1,right=shortLength-1;
int i,j;
//短数组左半部分最大值,长数组左半部分最大值,短数组右半部分最小值,长数组右半部分最小值
int max1=0,max2=0,min1=0,min2=0;
//左半部分最大值,右半部分最小值
int leftMax=0,rightMin=0;
while(left<=right){
i=(left+right)/2;
j=(int)(totalLength/2.0+0.5)-(i+1)-1;
//获得左半部分的最大值
max1=i>=0&&i<shortLength?shortArray[i]:Integer.MIN_VALUE;
max2=j>=0&&j<longLength?longArray[j]:Integer.MIN_VALUE;
leftMax=Math.max(max1,max2);
//获得右半部分的最小值
min1=(i+1)>=0&&(i+1)<shortLength?shortArray[i+1]:Integer.MAX_VALUE;
min2=(j+1)>=0&&(j+1)<longLength?longArray[j+1]:Integer.MAX_VALUE;
rightMin=Math.min(min1,min2);
if(leftMax<=rightMin){
break;
}
else if(max1>min2){
right=i-1;
}
else if(max2>min1){
left=i+1;
}
}
return totalLength%2==0?(leftMax+rightMin)/2.0:leftMax;
}
寻找单个有序序列的中位数
public double getMiddleNum(int[] nums, int left, int right){
if(left>right) return -1;
int middle=(left+right)/2;
boolean flag=(right-left+1)%2==0?true:false;
if(flag){
return (nums[middle]+nums[middle+1])/2.0;
}
else{
return nums[middle];
}
}