【题目】 :难度级别[困难]
给定两个大小为 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
[思路]
题目的要求时间复杂度 O(log(m+n)。看到 log,很明显,我们要考虑用二分的方法。
我们需要将两个数组进行切分,分成两个部分---左半部分和右半部分.
将i的左边和j的左边组合成[左半部分],将i的右边和j的右边组合成[右半部分].
1.当A数组和B数组的总长度是偶数时,如果我们能保证,左半部分的长度等于右半部分,
左半部分最大值小于等于右半部分的值max(A[i-1],B[j-1])<=min(A[i],B[j])
那么,中位数就是(左半部分最大值+右半部分最小值)/2。
2.当A数组和B数组的总长是奇数时,如果我们能保证,左半部分的长度比右半部分的大1,
左半部分最大值小于等于右半部分最小的值,那么中位数就是,左半部分最大值。
3.由于A数组和B数组是有序的,所以,A[i-1]<=A[i],B[i-1]<=B[i]一定成立,因此,只需
保证B[j-1]<=A[i]和A[i-1]<=B[j],所以分两种情况讨论:
3.1.B数组左边最大值大于A数组右边最小值,B [j - 1] > A [i],我们需要增加 i ,
为了数量的平衡还要减少 j .
3.2.A数组左边最大值大于B数组右边最小值,A [i - 1] > B [j],此时和上边的情况相反,
我们要减少i,增大j 。
//此方法leetCode提交,超时,不服,又提交两次依然超时
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m=nums1.length,n=nums2.length;//保证被切的数组元素个数最少
if(m>n){
return findMedianSortedArrays(nums2,nums1);
}
int leftArrLen=(m+n+1)/2;//初始化左数组组长度
int l=0,r=m;
int leftMax=Integer.MIN_VALUE,rightMin=Integer.MAX_VALUE;
int c1,c2,n2rightMin,n2leftMax,n1leftMax,n1rightMin;
while(l<=r){
c1=(1+r)/2;//数组1切点。。。这个地方写错了,也没有报错,导致后边修bug n个小时
c2=leftArrLen-c1;//数组2切点
//边界条件讨论
n1leftMax=c1==0?Integer.MIN_VALUE:nums1[c1-1];
n1rightMin=c1==m?Integer.MAX_VALUE:nums1[c1];
n2leftMax=c2==0?Integer.MIN_VALUE:nums2[c2-1];
n2rightMin=c2==n?Integer.MAX_VALUE:nums2[c2];
//如果满足左边数组小于右边数组
if(n1leftMax<=n2rightMin&&n1rightMin>=n2leftMax){
leftMax=Math.max(n1leftMax,n2leftMax);
rightMin=Math.min(n1rightMin,n2rightMin);
break;
}else if(n1leftMax>n2rightMin){//切点不合要求
r=c1-1;//右边界向左
}else{
l=c1+1;//左边界向右
}
}
return (m+n)%2==0?(leftMax+rightMin)*0.5:leftMax;//判断总个数,进行中位数计算
}
}
时间复杂度符合题目要求;空间复杂度O(1)
经过n小时的调试,终于发现了问题所在
原来把while循环中的小写字母l写成了1 !!!
贴出正确代码及运行结果
//正确代码(java)
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m=nums1.length,n=nums2.length;
if(m>n){
return findMedianSortedArrays(nums2,nums1);
}
int leftArrLen=(m+n+1)/2;
int l=0,r=m;
int leftMax=Integer.MIN_VALUE,rightMin=Integer.MAX_VALUE;
int c1,c2,n2rightMin,n2leftMax,n1leftMax,n1rightMin;
while(l<=r){
c1=(l+r)/2;
c2=leftArrLen-c1;
n1leftMax=c1==0?Integer.MIN_VALUE:nums1[c1-1];
n1rightMin=c1==m?Integer.MAX_VALUE:nums1[c1];
n2leftMax=c2==0?Integer.MIN_VALUE:nums2[c2-1];
n2rightMin=c2==n?Integer.MAX_VALUE:nums2[c2];
if(n1leftMax<=n2rightMin&&n1rightMin>=n2leftMax){
leftMax=Math.max(n1leftMax,n2leftMax);
rightMin=Math.min(n1rightMin,n2rightMin);
break;
}else if(n1leftMax>n2rightMin){
r=c1-1;
}else{
l=c1+1;
}
}
return (m+n)%2==0?(leftMax+rightMin)*0.5:leftMax;
}
}