[c++]LeetCode Median of Two Sorted Arrays问题

问题描述:

There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

总结大牛的算法如下:

假设nums1,nums2的大小分别为m,n。

将有序系列nums1分为左右两个部分,有m中分法,如下所示为第i中分法:

 |               |               |               |               |                   i                  |                  |                   |

 | nums1[0] | nums1[1] | nums1[2] | nums1[3]...| nums1[i-1] | nums1[i] ....| nums1[m-2] | nums1[m-1] |

同样对nums2有n种分法,讲nums1,nums2左边和左边的序列组合为一个新的集合numsLeft,同样可以得到numsRight

如果能满足一下两个条件,就能容易的得到中位数:

  1. numsLeft.size() == numsRight.size()  或者 numsLeft.size() == numsRight.size() +1
  2. 左边集合中的数 <= 右边集合中的数
假设nums1,nums2分别在i,j处分割,则要满足:
  1. i+j=m-i+n-j or i+j=m-i+n-j+1
  2. nums1[i-1] <= nums2[j]  and nums2[j-1] <= nums1[i]
对于i==0 or m, j==0 or n的情况  i-1,j-1会超出数组索引范围,需要讨论,所以思路如下,i从0到m依次递增,得到nums1的一种分法,由最终左右两边数字的个数相等,得到对应的nums2的分割方法,比较如果满足 nums1[i-1] <= nums2[j]  and nums2[j-1] <= nums1[i],则找到了符合要求的分法,跳出,求中位数。
我们必须保证nums1.size()<=nums2.size(),这样先对一个小的数组分割,不管怎么分都能对大的数组分割满足左右个数相等的要求。
代码如下:
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
       if(nums1.size() > nums2.size())
           return findMedianSortedArrays(nums2,nums1);
       decltype(nums1.size()) i=0,j=0,m=nums1.size(),n=nums2.size();
       
       if(m==0)
           if(n%2)
           return nums2[n/2];
           else
           return (nums2[n/2-1]+nums2[n/2])/2.0;

       for(i=0;i<=m;i++)
       {
           j=(n+m+1)/2-i;
           if(i==0 || j==n)
               if(nums1[i] >= nums2[j-1])
                   break;
               else
               continue;
           else if(i==m || j==0)
               if(nums1[i-1] <= nums2[j])
                   break;
               else
               continue;    
           else
           {
               if(nums1[i-1] <= nums2[j] && nums2[j-1] <=nums1[i])
                   break;
           }
       }
       cout<<"i  "<<i<<endl;
       cout<<"j  "<<j<<endl;
       //left set
       int numleft=0,numright=0;
       if(i==0)
           numleft=nums2[j-1];
       else if(j==0)
           numleft=nums1[i-1];
       else
           numleft=max(nums1[i-1],nums2[j-1]);
           
       if((n+m)%2)
           return numleft;
       else
       {
           if(i==m)
           numright=nums2[j];
           else if(j==n)
           numright=nums1[i];
           else 
           {
               numright=min(nums1[i],nums2[j]);
           }
           return (numleft+numright)/2.0;
       }
          
        
    }
};

说明:
  1. 对于从0开始编号的数组,如何取到中间的值? 如果m为奇数, nums[(m-1)/2],因为去尾的原因也可以直接写成nums[m/2],当m为偶数,是中间两个数的平均值 (nums[m/2]+nums[m/2-1])/2.0。
  2. 如果是从1开始编号的数组,m为奇数时应为nums1[(m+1)/2],当m为偶数时,为(nums[m/2]+nums[m/2+1])/2.0。
  3. 为什么j=(m+n)/2-i  或者 j=(m+n+1)/2-i,在程序中用j=(m+n+1)/2-i就可以完全表示,不需要分类讨论?因为当m+n为奇数时,左边要比右边多分一个数,j=(m+n+1)/2-i。当m+n为偶数时,(m+n+1)/2和(m+n)/2结果是一样的,所以不用分类讨论。
  4. 在3的问题上扩展,3/2==1, -3/2==-1,直接去尾
  5. 当nums1.size() > nums2.size()调换一下数组的位置即可
  6. 当m+n为奇数时,中间值出现在numsLeft,取max(nums1[i-1],nums2[j-1]),但是需要讨论边界情况,当m+n为偶数时,取numsLeft得最大值和numsRight的最小值,然后取平均即可,同样要考虑边界情况
以上的代码是i从0到m递增,一次验证分割方法是否满足要求,因为nums1,nums2都是有序数列,可以使用二分法进行搜索,修改如下:
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        if(nums1.size() > nums2.size())
            return findMedianSortedArrays(nums2,nums1);
        decltype(nums1.size()) i=0,imin=0,imax=nums1.size(),m=imax,n=nums2.size(),j=0,half=(m+n+1)/2;
        
        if(m == 0)
        {
            if(n%2)
                return nums2[n/2];
            else
                return (nums2[n/2-1]+nums2[n/2])/2.;
        }
        
        while(imin <= imax)
        {
            i=(imin+imax)/2;
            j=half-i;
            
            if(i ==0 || j == n)
            {
                if(nums1[i] < nums2[j-1])
                    imin=i+1;
                else break;
            }
            else if(j ==0 || i == m)
            {
                if(nums1[i-1] > nums2[j])
                    imax=i-1;
                else break;
            }
            else
            {
                if(nums1[i-1] > nums2[j])
                    imax=i-1;
                else if(nums2[j-1] > nums1[i])
                    imin=i+1;
                else
                    break;
            }
        }
        cout<<"i is  "<<i<<endl;
        cout<<"j is  "<<j<<endl;
        
        int numLeft=0,numRight=0;
        if(i==0)
            numLeft=nums2[j-1];
        else if(j==0)
            numLeft=nums1[i-1];
        else
            numLeft=max(nums1[i-1],nums2[j-1]);
        
        if((n+m)%2)
            return numLeft;
        else
        {
            if(i==m)
                numRight=nums2[j];
            else if(j==n)
                numRight=nums1[i];
            else
                numRight=min(nums1[i],nums2[j]);
            
            return (numLeft+numRight)/2.;
        }
        
        
    
        
    }
};

最后放一个大神的代码吧:
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(), n = nums2.size();
        if (m > n) return findMedianSortedArrays(nums2, nums1);
        int i, j, imin = 0, imax = m, half = (m + n + 1) / 2;
        while (imin <= imax) {
            i = (imin & imax) + ((imin ^ imax) >> 1);
            j = half - i;
            if (i > 0 && j < n && nums1[i - 1] > nums2[j]) imax = i - 1;
            else if (j > 0 && i < m && nums2[j - 1] > nums1[i]) imin = i + 1;
            else break;
        }
        int num1;
        if (!i) num1 = nums2[j - 1];
        else if (!j) num1 = nums1[i - 1]; 
        else num1 = max(nums1[i - 1], nums2[j - 1]);
        if ((m + n) & 1) return num1;
        int num2;
        if (i == m) num2 = nums2[j];
        else if (j == n) num2 = nums1[i];
        else num2 = min(nums1[i], nums2[j]);
        return (num1 + num2) / 2.0;
    }
};


















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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值