二分算法 4. 寻找两个正序数组的中位数

本文详细介绍了如何在O(log(m + n))的时间复杂度内找到两个正序数组nums1和nums2的中位数。通过二分法确定中位数位置,避免奇偶问题,优化为(n1+n2+1)/2,并分析了二分查找的不同策略,包括从小往大和从大往小的二分方法。

4. 寻找两个正序数组的中位数

给定两个大小为 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

解题
对两个数组进行二分求中位数:

关键点
中位数的位置一定是两个数的长度和的一半——故将两个数组看作整体;
得到中位数左边的数的个数——medium=(n1+n2)/2;{防止奇偶问题,优化为(n1+n2+1)/2};
划边界时,若划到nums1[i],则nums2必划到medium-i

二分
对i进行二分查找,即满足边界左边的两个数都小于边界右边的两个数;

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        
        //视情况交换nums1nums2,使nums1短于nums2
        if(nums1.size()>nums2.size()){
            vector<int> tmp=nums2;
            nums2=nums1;
            nums1=tmp;
        }
        int n1=nums1.size();
        int n2=nums2.size();

        int n=(n1+n2+1)/2;   //左边的个数

        //从第一个数组的0-m找分割线
        int left=0;
        int right=n1;

        while(left<right){
            int i=left+(right-left+1)/2;  //右取中位数,使得i-1不会越界
            int j=n-i;
            if(nums1[i-1]>nums2[j]){
                right=i-1;
            } else left=i;
        }

        int i=left;
        int j=n-i;
        int nums1left= i==0? INT_MIN:nums1[i-1];
        int nums1right= i==n1? INT_MAX:nums1[i];
        int num2left= j==0?INT_MIN:nums2[j-1];
        int num2right= j==n2?INT_MAX:nums2[j];
        if((n1+n2)%2) return max(nums1left,num2left);//奇数
        else return max(nums1left,num2left)/2.0+min(nums1right,num2right)/2.0;
    }
private:
    double res;
};

从小往大的二分

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        if(nums1.size()>nums2.size()){
            vector<int> tmp=nums2;
            nums2=nums1;
            nums1=tmp;
        }
        //将短的数组放到nums1
        int n1=nums1.size();
        int n2=nums2.size();
        //找中位数——则左边的个数为一半
        int Midieum=(n1+n2+1)/2; //找到中位数右边的下标,即左边的个数
        //对n1进行二分,找到边界线
        int left=0;
        int right=n1;
        while(left<right){
            int i=left+(right-left+1)/2;   //因为left=i,防止右边取不到,故取上底
            int j=Midieum-i; //j为左边的个数-n1的个数,即nums2边界右边的下标
            if(nums1[i-1]>nums2[j])//短数组边界左边大于长数组边界右边,往左边找
                right=i-1;
            else left=i;
        }
        //出来后,left=right=边界i
        int i=left; //边界右边的位置;
        int j=Midieum-i; //nums2边界右边的位置
        int nums1leftmax= i==0? INT_MIN: nums1[i-1];
        int nums1rightmin= i==n1? INT_MAX:nums1[i];
        int nums2leftmax= j==0?INT_MIN:nums2[j-1];
        int nums2rightmin= j==n2?INT_MAX:nums2[j];

        if((n1+n2)%2) return max(nums1leftmax,nums2leftmax);
        else return max(nums1leftmax,nums2leftmax)/2.0+min(nums1rightmin,nums2rightmin)/2.0;
    }
};

从大往小的二分

        int left=0;
        int right=n1;
        while(left<right){
            int i=left+(right-left)/2;
            int j=Midieum-i;
            if(nums1[i]<nums2[j-1])  //往右边走
                left=i+1;
            else right=i;
        }

关键点
将两个数组看作块分析,二分划线位置;

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值