Leetcode 4-寻找两个有序数组的中位数(hard)

题目描述

给定两个大小为 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;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值