4. Median of Two Sorted Arrays (Hard)

原题目:
  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)).
  Example 1:
  nums1 = [1, 3]
  nums2 = [2]
  The median is 2.0
  Example 2:
  nums1 = [1, 2]
  nums2 = [3, 4]
  The median is (2 + 3)/2 = 2.5
  
题目大意如下:
  有两个长度分别为m和n的有序的数组,要求在O(log (m+n))的时间复杂度内找到这两个数组的中位数
  
解题思路一:(归并)
  这是最容易想到的一种思路,先将两个数组合并到一起,然后求中位数,在O(n)的情况下过是能过,但是不能满足题目要求的O(log (m+n))。
  
代码如下:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        vector<int> ans ;
        ans.clear() ;

        //如果其中有一个数组为空的情况
        if(nums1.size() == 0){
            if(nums2.size()%2 == 0) return (double)(nums2[nums2.size()/2] + nums2[nums2.size()/2 - 1])/2 ;
            else return nums2[nums2.size()/2] ;
        } 
        if(nums2.size() == 0){
            if(nums1.size()%2 == 0) return (double)(nums1[nums1.size()/2] + nums1[nums1.size()/2 - 1])/2 ;
            else return nums1[nums1.size()/2] ;
        } 

        //两个都不为空,归并,然后求中位数
        int p = 0 , q = 0 ;
        while(p < nums1.size() && q < nums2.size()){
            if(nums1[p] < nums2[q]){
                ans.push_back(nums1[p]) ;
                p++ ;
            }
            else if(nums1[p] > nums2[q]){
                ans.push_back(nums2[q]) ;
                q++ ;
            }
            else{
                ans.push_back(nums2[q]) ;
                ans.push_back(nums2[q]) ;
                q++ ;
                p++ ;
            }
        }
        while(p < nums1.size()){
            ans.push_back(nums1[p]) ;
            p++ ;
        }
        while(q < nums2.size()){
            ans.push_back(nums2[q]) ;
            q++ ;
        }

        if(ans.size()%2 == 0)
            return (double)(ans[ans.size()/2]+ans[ans.size()/2 - 1])/2  ;
        else 
            return ans[ans.size()/2] ;
    }
};

运行结果:
运行结果

解题思路二:(分治)
  找中位数之前我们先理解一下中位数的定义:
  
  如果我们找到集合中的一个数,能把这个集合分为大小相等的两个子集,其中一个子集中的所有数都小于这个数,另外一个子集中的所有数都大于这个数,这个数就是中位数。
  
  假设我们现在有两个有序的集合A(大小为N1)和B(大小为N2),我们分别用i(i∈[0,N1])和j(j∈[0,N2])对A进行划分,得到由A[0]~A[i - 1]和B[0]~B[j - 1]组成的一个集合——姑且称之为左集合,同时还能得到由A[i]~A[N1 - 1]和B[j]~B[N2 - 1]组成的另一个集合——姑且称之为右集合
  
                左集合 | 右集合             
      A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[N1-1]
      B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[N2-1]

  
  现在我们只需两个条件即可找到中位数:
  ①左集合大小 == 右集合大小 ;
  ②左集合中最大数 <= 右集合中最小数 ;
  
  即:

  ①i + j == N1 - i + N2 - j + 1 (*因为有可能N1+N2为奇数所以要加上1)
  ②max(left_set)<= min(right_set);
  
  同时我们注意到:
    j = (N1 + N2 + 1)/2 - i ( N1 <= N2) ;
  因为i∈[0,N1],所以我们要保证 j > 0就得让N1 <= N2。
  
  接下来我们考虑如何找i,因为是有序的集合,所以很显然可以用二分查找
           i_min = 0 , i_max = N1 ;
             i = (i_min + i_max) / 2 ;

  进入循环时就需要考虑边界条件:
   ①i = 0即A集合全在右集合中;
   ②i = N1即A集合全在左集合中;
   ③j = 0即B集合全在右集合中 ;
   ④j = m即B集合全在左集合中 ;
  

while(i_min <= i_max){
    if(A[i - 1] > B[j] && i > 0 && j < N2)
    //以上情况表明我们找到的i太大了,需要减小i(因为原来集合是从小到大排序)
    else if(A[i] < B[j - 1] && i < N1 && j > 1)
    //以上情况表明我们找到的i太小了,需要增大i
    else
    ...
    //我们找到的i是符合条件的
}

最后我们还可以简略一点:
  因为 i < m 等价于 j > 0 并且 i > 0 等价于 j < n 。
所以最终代码如下:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        vector<int> A = nums1 ;
        vector<int> B = nums2 ;
        int N1 = A.size() , N2 = B.size() ;
        if(N1 > N2){
            swap(N1 , N2) ;
            A = nums2 ;
            B = nums1 ;
        } 
        //确保了A是较小的那个集合,B是较大的那个集合,从而确保j > 0

        int i_min = 0 , i_max = N1 ;
        int i , j ;
        int max_of_left , min_of_right ;

        while(i_min <= i_max){
            i = (i_min + i_max)/2 ;
            j = (N1 + N2 + 1)/2 - i ;
            //定义i和j

            if(i < N1 && B[j - 1] > A[i]){
                i_min = i + 1 ; 
                //我们找到的i太小所以要增大i
            }
            else if(i > 0 && A[i - 1] > B[j]){
                i_max = i - 1 ;
                //我们找到的i太大所以要减小i
            }
            else {
                if(i == 0) max_of_left = B[j - 1] ;
                else if(j == 0) max_of_left = A[i - 1] ;
                else max_of_left = max(B[j - 1] , A[i - 1]) ;
                //找到合适的i但要进行边界处理

                if( (N1 + N2)%2 ) return (double)max_of_left ;
                //如果N1 + N2为奇数就不需要再算右边的最小值

                if(i == N1) min_of_right = B[j] ;
                else if(j == N2) min_of_right = A[i] ;
                else min_of_right = min(B[j] , A[i]) ;

                return (double)(max_of_left + min_of_right)/2 ;
            }
        }
    }
};

运行结果:
这里写图片描述

算法分析:
  因为主要是用二分查找找i的过程,所以是O(logn),又因为n是min(N1,N2),所以最终时间复杂度应该是O(log(min(N1,N2)))。
  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值