这题要求的时间复杂度为O(log(m+n))O(log(m+n))O(log(m+n)),这一点感觉挺难想的,我打算是用递归将一个数组分割然后插入到另一个数组中,不过调了很久都没过,题解中的解法很巧妙,而且可以用在找两个有序数组中第kthk_{th}kth元素。
下面看一下中位数的作用:
将一个集合分成连个长度相等的子集合,其中一个子集合中的元素全部大于另一个子集合中的元素
随机将一个长度为mmm的数组分成两份,我们一共有m+1m+1m+1种分法。i=0→mi=0\to mi=0→m
left_A | right_A |
---|---|
A[0],A[1],.....,A[i−1]A[0],A[1],.....,A[i-1]A[0],A[1],.....,A[i−1] | A[i],A[i+1],.....,A[m−1]A[i],A[i+1],.....,A[m-1]A[i],A[i+1],.....,A[m−1] |
其中有len(left_A)=i,len(right_A)=m−ilen(left\_A)=i,len(right\_A)=m-ilen(left_A)=i,len(right_A)=m−i,当i=0i=0i=0的时候,左边部分为空,当i=mi=mi=m的时候,右半部分为空。
同理,若我们将数组BBB也随机分割:
left_B | right_B |
---|---|
B[0],B[1],.....,B[j−1]B[0],B[1],.....,B[j-1]B[0],B[1],.....,B[j−1] | B[j],B[j+1],.....,B[n−1]B[j],B[j+1],.....,B[n-1]B[j],B[j+1],.....,B[n−1] |
然后我们将两个数组的左半边和右半边进行合并
left_part | right_part |
---|---|
A[0],A[1],.....,A[i−1]A[0],A[1],.....,A[i-1]A[0],A[1],.....,A[i−1] | A[i],A[i+1],.....,A[m−1]A[i],A[i+1],.....,A[m-1]A[i],A[i+1],.....,A[m−1] |
B[0],B[1],.....,B[j−1]B[0],B[1],.....,B[j-1]B[0],B[1],.....,B[j−1] | B[j],B[j+1],.....,B[n−1]B[j],B[j+1],.....,B[n-1]B[j],B[j+1],.....,B[n−1] |
这时,如果满足条件:
- len(left_part)=len(right_part)len(left\_part)=len(right\_part)len(left_part)=len(right_part)
- max(left_part)≤min(right_part)max(left\_part)\leq min(right\_part)max(left_part)≤min(right_part)
那么这时候,我们已经将{A,B}\{A,B\}{A,B}分成了长度相等的两个集合,而且有一个集合中的元素总是大于另一个集合中的元素,这样我们就找到了中位数了,即median=max(left_part)+min(right_part)2median = \frac{max(left\_part)+min(right\_part)}{2}median=2max(left_part)+min(right_part)
为了满足上面两个条件,我们只需要满足:
- i+j=(m+n+1)2i+j = \frac {(m+n+1)}{2}i+j=2(m+n+1),即j=(m+n+1)2−ij=\frac {(m+n+1)}{2}-ij=2(m+n+1)−i
- A[i−1]≤B[j]A[i-1]\leq B[j]A[i−1]≤B[j] and B[j−1]≤A[i]B[j-1]\leq A[i]B[j−1]≤A[i]
这里我们假设有n>mn>mn>m,由于0≤i≤m0\leq i \leq m0≤i≤m,那么jjj就一定为一个非负数。(这里,要是我们将上面的条件改为i+j=ki+j=ki+j=k,那么就可以查找kthk_{th}kth的数)
所以我们只需要二分查找iii的位置,即可得到答案。步骤如下所示:
- 设置左边界L=0L=0L=0,右边界R=mR=mR=m,从区间[L,R][L,R][L,R]开始查找
- 让i=L+R2i=\frac {L+R}{2}i=2L+R,j=(m+n+1)2−ij=\frac {(m+n+1)}{2}-ij=2(m+n+1)−i
- 现在我们已经满足len(left_part)=len(right_part)len(left\_part)=len(right\_part)len(left_part)=len(right_part),只需满足第二个条件即可。
- 如果满足A[i−1]≤B[j]A[i-1]\leq B[j]A[i−1]≤B[j] and B[j−1]≤A[i]B[j-1]\leq A[i]B[j−1]≤A[i],那么我们已经得到了解,退出二分查找即可
- 如果有A[i−1]>B[j]A[i-1] > B[j]A[i−1]>B[j] ,说明我们的A[i−1]A[i-1]A[i−1]太大了,所以我们需要减小iii,由于减小iii时,jjj会增大,所以能找到我们要的解。而如果我们增大iii,同时jjj会变小,所以不能得到解,所以此时,我们应该让R=i−1R = i-1R=i−1,,然后继续查找
- 如果有B[j−1]>A[i]B[j-1] > A[i]B[j−1]>A[i] ,说明我们的A[i]A[i]A[i]太小了,所以我们需要增大iii,理由同上,所以此时,我们应该让L=i+1L = i+1L=i+1,然后继续查找
最后当iii找到的时候,分奇偶两种情况
- max(A[i−1],B[j−1])max(A[i-1],B[j-1])max(A[i−1],B[j−1]),当m+nm+nm+n为奇数
- max(A[i−1],B[j−1])+min(A[i],B[j])2\frac {max(A[i-1],B[j-1])+min(A[i],B[j])} {2}2max(A[i−1],B[j−1])+min(A[i],B[j]),当m+nm+nm+n为偶数
考虑到当i=0,i=m,j=0,j=ni=0,i=m,j=0,j=ni=0,i=m,j=0,j=n时,A[i−1],B[j−1],A[i],B[j]A[i-1],B[j-1],A[i],B[j]A[i−1],B[j−1],A[i],B[j]可能不存在,由于我们只是要判断是否满足条件,所以我们当元素不存在时,就不用进行判断了。也就是说,
- (j=0(j=0(j=0 || i=mi=mi=m || B[j−1]≤A[i]B[j-1] \leq A[i]B[j−1]≤A[i] && A[i−1]≤B[j])A[i-1] \leq B[j])A[i−1]≤B[j])
- (j>0(j>0(j>0 && i<mi<mi<m || B[j−1]>A[i]B[j-1] > A[i]B[j−1]>A[i])$
- (i>0(i>0(i>0 && j<nj<nj<n || A[i−1]>B[j]A[i-1] > B[j]A[i−1]>B[j])$
代码如下:
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
//我们需要保持 n>m
if(m>n)
{
swap(nums1,nums2);
swap(m,n);
}
//二分法查找i
int L = 0,R = m;
int len = (m+n+1)/2;
while(L<=R)
{
int i = (L + R)/2;
int j = len-i;
//i太小
if(j>0&&i<R&&nums2[j-1]>nums1[i])
{
L = i+1;
}
//i太大
else if(i>0&&j<n&&nums1[i-1]>nums2[j])
{
R = i-1;
}
//满足两个条件,我们得到了解
else
{
//首先求左边的最大值
int maxleft = 0;
if(i==0) {maxleft = nums2[j-1];}
else if(j==0){maxleft = nums1[i-1];}
else{maxleft = max(nums1[i-1],nums2[j-1]);}
//判断是否为奇数
if((m+n)%2==1) return maxleft;
int minright = 0;
if(i == m) minright = nums2[j];
else if(j == n) minright = nums1[i];
else{minright = min(nums1[i],nums2[j]);}
return (maxleft+minright)/2.0;
}
}
return 0.0;
}
};