2-找两个有序数组的中位数

本文探讨了寻找两个已排序数组中位数的有效算法。首先介绍了一种简单但效率较低的方法,通过合并两个数组并直接查找中位数;随后提出了一种更高效的解决方案,利用二分查找的思想,在O(log(m+n))的时间复杂度内解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0. 题目

题目: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)).

1. 步骤

假设这两个数组的大小分别为m (nums1)和n (nums2)。

  • 首先要有最差的解法,即不考虑时间复杂度O(log(m+n))
    • 解法1-思路:使用合并排序中的merge函数,将两个有序数组合并为一个有序数组,而后返回中位数。此时时间复杂度为O(n+m)
  • 其次,再找较优秀的解法
    • 解法2-思路: 时间复杂度为O(log(m+n),两个数组又是有序的数组,故考察的应该是二分查找;

2. 优化思路

题目其实可以拓展为求两个有序数组第k大元素。假设我们有一个函数可以用来返回合并数组的第k大元素,那么就变成了这样:

  • 如果 m+n 是奇数,则找合并数组的第(m+n)/2大元素
    • 如果 m+n 是偶数,则返回(m+n)/2大元素和第(m+n)/2+1大元素

    如何求两个有序数组的第k大元素? 我们可以每次取两个数组的前k/2个元素,比较两个数组的第k/2个元素的大小,而后决定删除哪一个数组的前k/2个元素。因为第k大元素肯定不可能在第2/k个元素较小的那个数组的前k/2个元素之中。算法步骤如下:

    • 每一次取nums1和nums2的前k/2个元素,比较nums1[k/2]nums2[k/2]的大小

      • if(nums1[k/2] <= nums2[k/2] ),则删除nums1[0~k/2]之间的元素,第k个元素不可能在该区间,同时也要更新k
      • 反之,则删除nums2[0~k/2]之间的元素,更新k
      • 递归地进行上述操作,直到找到第k个元素为止

    3. 容易出错的地方

    基本上就是递归的终止条件,非常容易出错。要注意如下几点:

    • nums1.size()<k/2nums2.size()<k/2的时候,要如何处理?
    • k==1的时候要如何处理?
    • 上述两个条件都不满足的时候要如何处理?
    • 每一删除数组中一个区间,数组的边界如何处理?

    4. 代码如下

    • 方法1:
    class Solution1 { //时间复杂度为o( size(x1) + size(x2) ) 
    public:
        void merge(vector<int> &des,vector<int>&x1,vector<int>&x2){
         int _size=des.size();
         int i,j,k;
         i=0;
         j=0;
         k=0;
         while(i<x1.size() && j<x2.size() ){
            if(x1[i]<x2[j]){
                des[k++]=x1[i++];
             }else{
                des[k++]=x2[j++];
             }
         }
    
         if(i<x1.size()) { 
            for(;i<x1.size();++i){
                des[k++]=x1[i];
             }
         }else{
            for(;j<x2.size();++j){
                des[k++]=x2[j];
             }
         }
    
      }  
    
      double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
           vector<int> des(nums1.size()+nums2.size(),0);
           merge(des,nums1,nums2);
           int middle=( des.size()-1 )/2;
           if(des.size()%2==0){
               return double(des[middle]+des[middle+1])/2;
           }else{
               return des[middle];
           }
      }
    };
    • 方法2:
    class Solution2 {
    public:
        // 非递归形式 边界这块不好写  不用考虑奇数偶数问题只需要返回一个值就好了 
       int findKth(vector<int>&nums1,int low1,int high1,vector<int>&nums2,int low2,int high2,int k){
            if(low1>high1)
                return nums2[low2+k-1];
            if(low2>high2)
                return nums1[low1+k-1];
            if(k==1)
            {
               return min(nums1[low1],nums2[low2]); 
             }
            int mid=k/2-1;
            if( (low1+mid)>high1){
                if( nums1[high1]<=nums2[low2+mid]  ){
                    k=k-(high1-low1+1);
                    return nums2[low2+k-1];
                }else{
                    k=k-(mid+1);
                    low2 += mid+1;
                    return findKth(nums1,low1,high1,nums2,low2,high2,k);
                }
    
             }
    
            if((low2+mid)>high2 ){
                if( nums2[high2]<=nums1[low1+mid]  ){
                    k=k-(high2-low2+1);
                    return nums1[low1+k-1];
                }else{
                    k=k-(mid+1);
                    low1 += mid+1;
                    return findKth(nums1,low1,high1,nums2,low2,high2,k);
                }
            }
    
            if( nums1[ low1 + mid ] <= nums2[ low2 + mid ]) {
                 low1=low1+mid+1;
                 k=k-(mid+1);
            }else{
                 low2=low2+mid+1;
                 k=k-(mid+1);
            }
            return findKth(nums1,low1,high1,nums2,low2,high2,k);
       } 
      double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
          int _size=nums1.size()+nums2.size();
          int mid=(_size+1)/2;
          double res=0;
          if(_size%2==0){
                res=(double)findKth(nums1,0,nums1.size()-1,nums2,0,nums2.size()-1,mid);
                cout<<res<<endl;
                res += (double)findKth(nums1,0,nums1.size()-1,nums2,0,nums2.size()-1,mid+1);
                res=res/2;
          }else{
                res=(double)findKth(nums1,0,nums1.size()-1,nums2,0,nums2.size()-1,mid);
          }
          return res;
      }
    };
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值