LeetCode:4.寻找两个正序数组的中位数(C++实现)

原题:4. 寻找两个正序数组的中位数 - 力扣(LeetCode)

注意:题目给我们的两个数组是正序(已经从小到大排好序的了)

本文提供了三个方法,理解难度递升。

//方法一:直接法
class Solution {
public:
    //因为中位数的情况要么是整数,要么是小数,因此返回类型应该是更精确的
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        vector<double>vec(nums1.size()+nums2.size());//创建合并数组
        int i=0;//vec的索引
        int j=0;//nums1的索引
        int k=0;//nums2的索引
        
        //合并数组
        while(j<nums1.size()&&k<nums2.size()){
            vec[i++]=nums1[j]<nums2[k]?nums1[j++]:nums2[k++];
        }
        while(j<nums1.size()){//nums1数组还有剩余元素
            vec[i++]=nums1[j++];
        }
        while(k<nums2.size()){//nums2数组还有剩余元素
            vec[i++]=nums2[k++];
        }
        if(vec.size()%2!=0){//vec数组元素个数为奇数,直接返回中间的元素就好了
            return vec[vec.size()/2];
        }else{//vec数组元素个数为偶数,返回中间两个元素的平均值
            return (vec[vec.size()/2-1]+vec[vec.size()/2])/2.0;
            //注意这里是2.0而不是2,因为整数只有和一个小数做运算,才会得到一个小数
        }
    }
};
//时间复杂度:O(n)
//空间复杂度:O(n)

//方法二:分治(无需合并数组)
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int index1=0;
        int index2=0;

        int preVal=0;//preVal:记录当前目标值
        int postVal=0;//postVl:记录上一次的目标值

        int len=nums1.size()+nums2.size();
        for(int i=0;i<=len/2;i++){//由于取中位数,因此我们移动总数的一半
            postVal=preVal;//保存前一次的值
            //哪个数组的值小,该数组的索引就向后移动
            if(index1<nums1.size()&&(index2>=nums2.size()||nums1[index1]<nums2[index2])){
                /*判断条件:1.索引肯定要比当前数组长度小
                          2.如果index2已经到nums2最后一位,只有nums1还有多的元素,只能遍历nums1了
                          3.nums1当前值比nums2当前值小才能让nums1的索引index1后移
                */
                preVal=nums1[index1++];
            }else{
                preVal=nums2[index2++];
            }
        }

        if(len%2==0){//偶数个元素,返回平均值
            return (preVal+postVal)/2.0;//主要要除小数,这样才能得到double
        }else{//奇数个元素,返回一个值即可,即nums[len/2],也就是preVal
            return preVal;
        }
    }
};
//时间复杂度:O(n)
//空间复杂度:O(1)

//方法三:类二分(递归)
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int n=nums1.size();
        int m=nums2.size();
        int left=(n+m+1)/2;
        int right=(n+m+2)/2;
        //如果n+m是奇数,那么left=right;
        return (getKth(nums1,0,n-1,nums2,0,m-1,left)+getKth(nums1,0,n-1,nums2,0,m-1,right))*0.5;
    }

    //得到第k小的数
    //利用递归的方法寻找第k小的数,本质上就是第一次之后的递归都是建立在删掉前k/2(k是动态变化的)个小的元素之后的“新数组”上再次进行递归。因为前k/2个小的元素必不可能存在第k小的元素,所以删掉也无妨。但要注意:每次递归的起始位置已经改变,因为是“新数组”
    
    //退出递归的条件有两个:
    //1.已经访问到nums1数组的最后一位,直接返回nums2数组的第k小数即可,这里的k是已经动态变化过的了。
    //2.k=1,即返回当前两个数组最小的数即可,那让当前从两个数组各自访问到的元素比较,返回小的那个就行。
    int getKth(vector<int>&nums1,int start1,int end1,vector<int>&nums2,int start2,int end2,int k){
        int len1=end1-start1+1;
        int len2=end2-start2+1;

        //让len1小于len2,这样能保证有数组空时,一定是nums1
        if(len1>len2)return getKth(nums2,start2,end2,nums1,start1,end1,k);
        
        //nums1为空
        if(len1==0)return nums2[start2+k-1];
        
        //返回第一小的数(即最小的数)
        if(k==1)return min(nums1[start1],nums2[start2]);

        //取min是防止访问溢出
        int i=start1+min(len1,k/2)-1;//加min是防止越界访问
        int j=start2+min(len2,k/2)-1;
        if(nums1[i]>nums2[j]){
            return getKth(nums1,start1,end1,nums2,j+1,end2,k-(j-start2+1));
        }else{
            return getKth(nums1,i+1,end1,nums2,start2,end2,k-(i-start1+1));
        }
    }
};
//时间复杂度:O(logn)
//空间复杂度:O(1),因为用到的是尾递归,不需要不断地调用堆栈。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jomo.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值