思路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];
}
}