题目描述
中位数是将两个集合划分为两个长度相等的子集,其中一个子集的元素总是大于另一个子集中的元素。
设两个升序排列的有序数组A与B,A中包含m个元素,B中包含n个元素,请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
解题思路
A[0]………………A[i-1] | A[i]………………A[m-1]
B[0]………………B[j-1] | B[j]………………B[n-1]
记数组A,B的长度分别为m,n,不妨设m<=n,A,B升序排列。i,j分别将A,B分成两个部分。A[i]左侧的部分记作left_A,其他记作right_A,同理,B[j]左侧的部分记作left_B,其他记作right_B。为保证上述的条件,我们在代码中做如下处理:
/**
* 确保A的长度小于等于B的长度
*/
int m = A.length;
int n = B.length;
if (m > n) { // to ensure m<=n
int[] temp = A;
A = B;
B = temp;
int tmp = m;
m = n;
n = tmp;
}
对于A,B有四种情况:
[1]、m+n是偶数时,m是偶数,n是偶数
[2]、m+n是偶数时,m是奇数,n是奇数
[3]、m+n是奇数时,m是偶数,n是奇数
[4]、m+n是偶数时,m是奇数,n是偶数
现在假设一种理想的情况,令i=m/2,j=(m+n)/2-i,且A[i]>=B[j-1],A[i-1]<=B[j],即left_A+left_B全部小于A[i]和B[j],由i与j的位置可知,对于[1]、[2] ,left_A+left_B=right_A+right_B,对于[3]、[4],left_A+left_B+1=right_A+right_B,因为中位数被放到了右侧的部分,此时
对于[1]、[2]两种情况,中位数
对于[3]、[4]两种情况,中位数
但实际上如此理想的情况基本不会存在,因此我们需要调整i,j的位置。
当A[i]<B[j-1]时,必然有A[i-1]<B[j],这时,需要增大i,即令i指向的位置向右移,在增大i的同时必然会导致j的减小,因为j=(m+n)/2-i,确保left_A+left_B包含A,B两个数组一半的元素(m+n是偶数时,若m+n是奇数,left_A+left_B+1=right_A+right_B)。我们可以令i=i+1,j=j-1。
当A[i-1]>B[j]时,必然有A[i]>B[j-1],这时,需要减小i。令i=i-1,j=j+1。
调整i,j的位置后,重新判断是否满足条件A[i]>=B[j-1],A[i-1]<=B[j]。
所以上述过程可以写作如下的形式,
1、if A[i]>=B[j-1],A[i-1]<=B[j]
if m+n是偶数
median=(max(A[i-1],B[j-1])+min(A[i],B[j]))/2
if m+n是奇数
min(A[i],B[j])
2、if A[i]<B[j-1]
i=i+1,j=j-1
重复1
3、if A[i-1]>B[j]
i=i-1,j=j+1
重复1
while (i>=0&&i<m) {
if (i-1>=0){
if (A[i]>=B[j-1]&&A[i-1]<=B[j]) {
return (m+n)%2==0?
(Math.max(A[i-1],B[j-1])+Math.min(A[i],B[j]))/2.0
:Math.min(A[i],B[j]);
} else if (A[i]<B[j-1]) {
min = i;
i=(max+min)/2;
j=(m+n)/2-i;
} else if (A[i-1]>B[j]) {
max = i;
i=(max+min)/2;
j=(m+n)/2-i;
}
}
}
当然上面没有考虑边界的情况,即i=0和i=m-1时的情况,下面讨论边界的情况。
当i=0时,i-1会超出数组A的下界,这时我们只需判断A[i]>=B[j-1]是否成立,若有A[i]>=B[j-1]
if m+n是偶数
median=(B[j-1]+min(A[i],B[j]))/2
if m+n是奇数
median=min(A[i],B[j])
if (i==0) {
if (A[i]>=B[j-1]) {
return (m+n)%2==0? (B[j-1]+Math.min(A[i],B[j]))/2.0
:Math.min(A[i],B[j]);
}
}
若A[i]> B[j],这时,i需要继续减小,i会小于0,出现这种情况时, A中的元素全部大于B中的元素。
当i=m-1时,i-1不会超出下界,只需注意A[m-1]<=B[0]时的情况,当A[m-1]<=B[0]时,A中的元素全部小于B。
我们需要注意j的值是否超过上界或者下界吗?
当m<n时,不需要关注,因为这时j一定不会超出上界或者下界,只需要注意当m=n且A中元素全部大于或小于B时的情况,若j会超出上界。这种情况我们提前进行处理。
/**
* 处理A全部大于B或A全部小于B的情况
*/
if (A[0]>=B[n-1]) { //A 全部属于right_part;
if ((m+n)%2==0) { //m+n是偶数时
if (m==n) { //max{right_part} = A[0],min{left_part} = B[n-1]
return (A[0] + B[n - 1])/2.0;
} else {
return (B[(m+n)/2]+B[(m+n)/2-1])/2.0;
}
} else {
return B[(m+n)/2];
}
} else if (A[m-1]<=B[0]) { //A 全部属于left_part;
if ((m+n)%2==0) { //m+n是偶数
if (m==n) { //max{left_part} = A[m-1],min{right_part} = B[0]
return (A[m-1] + B[0])/2.0;
} else {
return (B[(n-m)/2]+B[(n-m)/2-1])/2.0;
}
} else { //m+n是奇数
return (B[(n-m)/2]);
}
}
同时,我们要注意到,要求时间复杂度为O(log(m + n)),所以在增大或者减小i时我们采用二分查找。我们知道二分查找的时间复杂度为O(log(2n)),采用二分查找的方法,时间复杂度为O(log(m))。
if (A[i]<B[j-1]) {
min = i;
i=(max+min)/2;
j=(m+n)/2-i;
} else if (A[i-1]>B[j]) {
max = i;
i=(max+min)/2;
j=(m+n)/2-i;
}
代码实现
private static double findMedianSortedArrays(int[] A, int[] B){
/**
* 确保A的长度小于等于B的长度
*/
int m = A.length;
int n = B.length;
if (m > n) { // to ensure m<=n
int[] temp = A;
A = B;
B = temp;
int tmp = m;
m = n;
n = tmp;
}
/**
* 处理A全部大于B或A全部小于B的情况
*/
if (A[0]>=B[n-1]) { //A 全部属于right_part;
if ((m+n)%2==0) { //m+n是偶数时
if (m==n) { //max{right_part} = A[0],min{left_part} = B[n-1]
return (A[0] + B[n - 1])/2.0;
} else {
return (B[(m+n)/2]+B[(m+n)/2-1])/2.0;
}
} else {
return B[(m+n)/2];
}
} else if (A[m-1]<=B[0]) { //A 全部属于left_part;
if ((m+n)%2==0) { //m+n是偶数
if (m==n) { //max{left_part} = A[m-1],min{right_part} = B[0]
return (A[m-1] + B[0])/2.0;
} else {
return (B[(n-m)/2]+B[(n-m)/2-1])/2.0;
}
} else { //m+n是奇数
return (B[(n-m)/2]);
}
}
int max = m;
int min = 0;
int i = (max+min)/2;
int j = (m+n)/2-i;
while (i>=0&&i<m) {
if (i==0) {
if (A[i]>=B[j-1]) {
return (m+n)%2==0? (B[j-1]+Math.min(A[i],B[j]))/2.0
:Math.min(A[i],B[j]);
}
}
if (i-1>=0){
if (A[i]>=B[j-1]&&A[i-1]<=B[j]) {
return (m+n)%2==0? (Math.max(A[i-1],B[j-1])+Math.min(A[i],B[j]))/2.0
:Math.min(A[i],B[j]);
} else if (A[i]<B[j-1]) {
min = i;
i=(max+min)/2;
j=(m+n)/2-i;
} else if (A[i-1]>B[j]) {
max = i;
i=(max+min)/2;
j=(m+n)/2-i;
}
}
}
return 0.0;
}