LeetCode(C++):寻找两个有序数组的中位数

题目描述:

给定两个大小为 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、因为是两个有序数组,所以需要两个割点C1,C2。若将两个数组(都是升序排序的数组)分别在一个割点处分为两半,那么L1(数组前半部分的最右边的元素)一定是小于R1(数组后半部分最左边的元素),同理L2<R2。

因为要找两个有序数组的中位数(为了方便奇偶性的区分,我们将两个数组分别虚拟设置为奇数,所以两个数组看为一个数组的时候,元素个数为偶数,中位数=(m1+m2)/2,此时只要找到m1,m2中位数便可以求出),若割点刚好处于中间,此时只需要找到当L1<R2,L2<R1的时候(两个数组前半部分都保证了比后半部分小的时候),比较L1和L2的数值,找选出最大的(因为左半部分从左到右是依次增大,那么找出最靠近右边的那个数m1)。

同理当R1>L2,R2>L1时候,比较R1和R2的数值,找出最小的(因为右半部分从左到右依次增大,那么找出最靠近左边的那个数m2)。此时(m1+m2)/2便是要求的中位数。

2、首先一个数组要求中位数,必须是排序好,而且要分奇偶,才能求出中位数。现在是两个数组,奇偶性都在不确定,所以原博主很明智地将数组进行了处理,通过虚拟地加入#符号,使得数组恒为奇。(例如:1,2,48,90  虚拟加#符号后变为:#1#2#48#90#),此处的处理是通过公示:2n+1始终是奇数而来的。代码中并没有实际加入符号#,只是将数组的长度进行了2n+1的翻倍。

3、所以只要找到处于中间的那个割点K,并找到K左右的两个数值,就可以求出中位数。对于单个有序数组,K的位置就是在数组长度一半的位置(当然要分奇偶)。偶数的话就是K左右的两个数值相加除以2,奇数的话,也就是K所在的位置。那么对于两个有序数组的话,如果left1,left2(两个数组的左半部分)的元素个数相加等于K的时候,比较L1,L2找出最大的那个数值,那个数值就是K左边的值,同理,K右边的值就是R1,R2中最小的那个。若假设数组1的割点为C1,数组2的隔点为C2,那么当C1+C2 = K的时候,就可以进行比较。

4、比较的时候,如果L1 > R2的时候,就需要将C1往左移动(左边数值更小),将C2往右移动(右边数值更大),直到满足L1<R2.

同理,如果L2 > R1的时候,就需要将C2往左移动,C1往右边移动,直到满足L2<R1。

此处需要考虑越界的问题:

若当C1=0,移动到了最左边或者C2移动到了最右边,都还不满足L1 < R2的时候,说明数组1 > 数组2,此时中位数在数组2中,若当C2=0,移动到了最左边或者C1移动到了最右边,还不满足L2<R1的时候,说明数组1 < 数组2,此时中位数在数组1中。

5、假设数组1长度为n,数组2长度为m,并且n<m(从最小长度进行二分)。

将数组长度进行虚处理,然后再把两个数组看作是数组A。

此时长度为(2m+1)+(2n+1)=2m+2n+2 此时从最中间隔开,k = (2m+2n+2)/2=(m+n+1),因为数组A是从下标0开始计数的,所以在程序中K实际等于(m+n).知道K值之后,C1,C2任意知道其中一个,便可以确定另外一个,此处对长度较小的数组进行二分,所以确定C1后再确定C2, C2 = k-C1=m+n-C1.此时我们将数组进行了虚拟的加#,所以要确定原来数组元素的真正位置,就包含一个映射关系。L1 =(C1-1)/2   L2=(C2-1)/2  R1=(C1/2) R2=(C2/2)
--------------------- 
作者:Jialuhu 
来源:CSDN 
原文:https://blog.csdn.net/qq_36573828/article/details/80752575 

 

代码如下:

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size();
        int m = nums2.size();
        if(n > m)
        {
            return findMedianSortedArrays(nums2,nums1);
        }
        int L1,L2,R1,R2,c1,c2,lo = 0, hi = 2 * n;
        while(lo <= hi)
        {
            c1 = (lo + hi)/2;  
            c2 = m + n- c1;
            L1 = (c1 == 0) ? INT_MIN:nums1[(c1-1)/2];   
            R1 = (c1 == 2*n) ? INT_MAX:nums1[c1/2];
            L2 = (c2 == 0) ? INT_MIN:nums2[(c2-1)/2];
            R2 = (c2 == 2*m) ? INT_MAX:nums2[c2/2];
            if(L1 > R2)
                hi = c1 - 1;
            else if(L2 > R1)
                lo = c1 + 1;
            else
                break;
        }
        return (max(L1,L2)+ min(R1,R2))/2.0;
    }
};

码源: 

--------------------- 
作者:Vosky 
来源:CSDN 
原文:https://blog.csdn.net/hk2291976/article/details/51107778 
版权声明:本文为博主原创文章,转载请附上博文链接!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值