LeetCode 第四题 Median of Two Sorted Arrays(计算两个有序数组的中位数)

该博客介绍了如何在O(log(m+n))的时间复杂度内找到两个有序数组的中位数,利用二分法寻找满足特定条件的数组分割点,确保左右两边长度相等且满足大小关系。通过不断调整边界,最终确定中位数位置,并给出奇偶情况下中位数的计算方式。

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

  这题要求的时间复杂度为O(log(m+n))O(log(m+n))O(log(m+n)),这一点感觉挺难想的,我打算是用递归将一个数组分割然后插入到另一个数组中,不过调了很久都没过,题解中的解法很巧妙,而且可以用在找两个有序数组中第kthk_{th}kth元素。
  下面看一下中位数的作用:

将一个集合分成连个长度相等的子集合,其中一个子集合中的元素全部大于另一个子集合中的元素

  随机将一个长度为mmm的数组分成两份,我们一共有m+1m+1m+1种分法。i=0→mi=0\to mi=0m

left_Aright_A
A[0],A[1],.....,A[i−1]A[0],A[1],.....,A[i-1]A[0],A[1],.....,A[i1]A[i],A[i+1],.....,A[m−1]A[i],A[i+1],.....,A[m-1]A[i],A[i+1],.....,A[m1]

其中有len(left_A)=i,len(right_A)=m−ilen(left\_A)=i,len(right\_A)=m-ilen(left_A)=ilen(right_A)=mi,当i=0i=0i=0的时候,左边部分为空,当i=mi=mi=m的时候,右半部分为空。
  同理,若我们将数组BBB也随机分割:

left_Bright_B
B[0],B[1],.....,B[j−1]B[0],B[1],.....,B[j-1]B[0],B[1],.....,B[j1]B[j],B[j+1],.....,B[n−1]B[j],B[j+1],.....,B[n-1]B[j],B[j+1],.....,B[n1]

  然后我们将两个数组的左半边和右半边进行合并

left_partright_part
A[0],A[1],.....,A[i−1]A[0],A[1],.....,A[i-1]A[0],A[1],.....,A[i1]A[i],A[i+1],.....,A[m−1]A[i],A[i+1],.....,A[m-1]A[i],A[i+1],.....,A[m1]
B[0],B[1],.....,B[j−1]B[0],B[1],.....,B[j-1]B[0],B[1],.....,B[j1]B[j],B[j+1],.....,B[n−1]B[j],B[j+1],.....,B[n-1]B[j],B[j+1],.....,B[n1]

这时,如果满足条件:

  • len(left_part)=len(right_part)len(left\_part)=len(right\_part)len(left_part)=len(right_part)
  • max(left_part)≤min(right_part)max(left\_part)\leq min(right\_part)max(left_part)min(right_part)

那么这时候,我们已经将{A,B}\{A,B\}{A,B}分成了长度相等的两个集合,而且有一个集合中的元素总是大于另一个集合中的元素,这样我们就找到了中位数了,即median=max(left_part)+min(right_part)2median = \frac{max(left\_part)+min(right\_part)}{2}median=2max(left_part)+min(right_part
为了满足上面两个条件,我们只需要满足:

  • i+j=(m+n+1)2i+j = \frac {(m+n+1)}{2}i+j=2(m+n+1),即j=(m+n+1)2−ij=\frac {(m+n+1)}{2}-ij=2(m+n+1)i
  • A[i−1]≤B[j]A[i-1]\leq B[j]A[i1]B[j] and B[j−1]≤A[i]B[j-1]\leq A[i]B[j1]A[i]

      这里我们假设有n>mn>mn>m,由于0≤i≤m0\leq i \leq m0im,那么jjj就一定为一个非负数。(这里,要是我们将上面的条件改为i+j=ki+j=ki+j=k,那么就可以查找kthk_{th}kth的数)
    所以我们只需要二分查找iii的位置,即可得到答案。步骤如下所示:
  • 设置左边界L=0L=0L=0,右边界R=mR=mR=m,从区间[L,R][L,R][L,R]开始查找
  • i=L+R2i=\frac {L+R}{2}i=2L+Rj=(m+n+1)2−ij=\frac {(m+n+1)}{2}-ij=2(m+n+1)i
  • 现在我们已经满足len(left_part)=len(right_part)len(left\_part)=len(right\_part)len(left_part)=len(right_part),只需满足第二个条件即可。
    • 如果满足A[i−1]≤B[j]A[i-1]\leq B[j]A[i1]B[j] and B[j−1]≤A[i]B[j-1]\leq A[i]B[j1]A[i],那么我们已经得到了解,退出二分查找即可
    • 如果有A[i−1]>B[j]A[i-1] > B[j]A[i1]>B[j] ,说明我们的A[i−1]A[i-1]A[i1]太大了,所以我们需要减小iii,由于减小iii时,jjj会增大,所以能找到我们要的解。而如果我们增大iii,同时jjj会变小,所以不能得到解,所以此时,我们应该让R=i−1R = i-1R=i1,,然后继续查找
    • 如果有B[j−1]>A[i]B[j-1] > A[i]B[j1]>A[i] ,说明我们的A[i]A[i]A[i]太小了,所以我们需要增大iii,理由同上,所以此时,我们应该让L=i+1L = i+1L=i+1,然后继续查找

最后当iii找到的时候,分奇偶两种情况

  • max(A[i−1],B[j−1])max(A[i-1],B[j-1])max(A[i1],B[j1]),当m+nm+nm+n为奇数
  • max(A[i−1],B[j−1])+min(A[i],B[j])2\frac {max(A[i-1],B[j-1])+min(A[i],B[j])} {2}2max(A[i1],B[j1])+min(A[i],B[j]),当m+nm+nm+n为偶数

  考虑到当i=0,i=m,j=0,j=ni=0,i=m,j=0,j=ni=0,i=m,j=0,j=n时,A[i−1],B[j−1],A[i],B[j]A[i-1],B[j-1],A[i],B[j]A[i1],B[j1],A[i],B[j]可能不存在,由于我们只是要判断是否满足条件,所以我们当元素不存在时,就不用进行判断了。也就是说,

  • (j=0(j=0(j=0 || i=mi=mi=m || B[j−1]≤A[i]B[j-1] \leq A[i]B[j1]A[i] && A[i−1]≤B[j])A[i-1] \leq B[j])A[i1]B[j])
  • (j&gt;0(j&gt;0(j>0 && i&lt;mi&lt;mi<m || B[j−1]&gt;A[i]B[j-1] &gt; A[i]B[j1]>A[i])$
  • (i&gt;0(i&gt;0(i>0 && j&lt;nj&lt;nj<n || A[i−1]&gt;B[j]A[i-1] &gt; B[j]A[i1]>B[j])$

  代码如下:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m  = nums1.size();
        int n = nums2.size();
        
        //我们需要保持 n>m
        if(m>n)
        {
            swap(nums1,nums2);
            swap(m,n);
        }
        
        //二分法查找i  
        int L = 0,R = m;
        int len = (m+n+1)/2;
        while(L<=R)
        {
            int i = (L + R)/2;
            int j = len-i;
            //i太小
            if(j>0&&i<R&&nums2[j-1]>nums1[i])
            {
                L = i+1;
            }
            //i太大
            else if(i>0&&j<n&&nums1[i-1]>nums2[j])
            {
                R = i-1;
            }
            //满足两个条件,我们得到了解
            else
            {
                //首先求左边的最大值
                int maxleft = 0;
                if(i==0) {maxleft = nums2[j-1];}
                else if(j==0){maxleft = nums1[i-1];}
                else{maxleft  = max(nums1[i-1],nums2[j-1]);}
                //判断是否为奇数
                if((m+n)%2==1) return maxleft;
                
                int minright = 0;
                if(i == m) minright = nums2[j];
                else if(j == n) minright = nums1[i];
                else{minright = min(nums1[i],nums2[j]);}
                
                return (maxleft+minright)/2.0;
            }
        }
        
        return 0.0;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值