题目描述
给定两个大小为 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
解法一
步骤:
1.先确保m<=n,如果不满足 就把两者都交换(目的是为了使后面j总是>=0)
2.i作为nums1的标记,j作为nums2的标记,用halflen表示两个数组拼起来的一半长度,分为左边一半和右边的一半。
3.为了求中位数,那么就需要左边一半的max小于右边一半的min。如果nums1[i-1]>nums2[j],这里为保证两半长度是一样的,i就要增大,j相应的减小(这样把nums1中大的数放到右边,nums2的小的数放到左边,通过移动 i 这个标记来实现这个操作)。如果nums[j-1]>nums1[i],同理就是j要增大而i减小。
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m=nums1.size(),n=nums2.size();
if(m==0)
{
if(n%2==1)
return nums2[n/2];
return (nums2[n/2]+nums2[n/2-1])/2.0;
}
if(n==0)
{
if(m%2==1)
return nums1[m/2];
return (nums1[m/2]+nums1[m/2-1])/2.0;
}
if(m>n) //为了保证m<=n,如果m>n,就让两者交换
{
vector<int> num;
num=nums1;nums1=nums2;nums2=num;
int temp;
temp=m;m=n;n=temp;
}
int imin=0,imax=m, halflen=(m+n+1)/2;
int lmax=0,rmin=0;
while(imin<=imax)
{
int i=(imin+imax)/2; //nums1的标记
int j=halflen-i; //nums2的标记
if(i<m & nums2[j-1]>nums1[i])
{
imin=i+1;
}
else if(i>0 & nums1[i-1]>nums2[j])
imax=i-1;
else
{
if(i==0)
lmax=nums2[j-1];
else if(j==0)
lmax=nums1[i-1];
else
lmax=max(nums1[i-1],nums2[j-1]);
if((m+n)%2==1)
return lmax;
if (i==m)
rmin=nums2[j];
else if(j==n)
rmin=nums1[i];
else
rmin=min(nums1[i],nums2[j]);
return (lmax+rmin)/2.0;
}
}
}
};
这个解法的时间复杂度是O(log(min(m,n)).
方法二:这个思路更简单了
这个方法先建立一个res数组,
将小的数先放入res中,当其中一个数组中的数都放完了之后,把另一个数组中未存入的数组补齐放入res中
但是这个解法的时间复杂度是O(m+n)
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m=nums1.size(),n=nums2.size();
if(m==0)
{
if(n%2==1)
return nums2[n/2];
return (nums2[n/2]+nums2[n/2-1])/2.0;
}
if(n==0)
{
if(m%2==1)
return nums1[m/2];
return (nums1[m/2]+nums1[m/2-1])/2.0;
}
vector<int> res;
int i=0,j=0;
while(i<m&&j<n) //将小的数先存入res中
{
if(nums1[i]<=nums2[j])
{
res.push_back(nums1[i]);
i++;}
else
{
res.push_back(nums2[j]);
j++;}
}
while(i<m) //也就是当经过上述j=n后,如果nums1中的数组还未存放完继续放入res中
{
res.push_back(nums1[i]);
i++;
}
while(j<n)
{
res.push_back(nums2[j]);
j++;
}
int len=m+n;
if(len%2==1)
return res[len/2]*1.0;
else
return (res[len/2]+res[len/2-1])/2.0;
}
};
解法三:
时间复杂度是log,数组是有序的,其实第一个要考虑的就是二分法。这个方法可以输出第k个数。
这里就是把数组一半一半的排除掉,每次循环可以去掉k/2个数。
先假设两个数组的长度都大于k/2,那么如果s[i-1] >l[j-1],这就说明了 l 的 0到j-1 共j个数都比s[i-1]要小,
也就是说 l 这个数组中至少有k/2个数比s[i-1]要小,
而s这个数组中,0到i-2 共i-1也就是有k/2-1个数也比s[i-1]要小,
就是一共有k/2+k/2-1,共k-1个数比s[i-1]小,
但由于不能判断s的前i-1位数与 l 的j位之后的数的大小,所以要先把 l 的 0到j-1 共j个数去掉不考虑。
由于去掉了j个数,所以k的值也要减少j,这是因为要找第k个小的数,而前j小的数已经确定了,所以要在剩下的数组中找k-j小的数。
于是就返回getkth(s,m,l+j,n-j,k-j)。
class Solution {
public:
int getkth(int s[],int m,int l[],int n,int k){
if(m>n)
return getkth(l,n,s,m,k);
if(m==0)
return l[k-1];
if(k==1)
return min(s[0],l[0]);
int i=min(m,k/2),j=min(n,k/2);
if(s[i-1]>l[j-1])
return getkth(s,m,l+j,n-j,k-j);
else
return getkth(s+i,m-i,l,n,k-i);
return 0;
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m=nums1.size(),n=nums2.size();
int l = (m + n + 1) >> 1; //右移1位相当于除以2
int r = (m + n + 2) >> 1;
int *a=m>0?&nums1[0]:NULL;
int *b=n>0?&nums2[0]:NULL;
return (getkth(a, m ,b, n, l) + getkth(a, m, b, n, r)) / 2.0;
}
};