leetcode 腾讯50题 2/50求两个有序数组的中位数

题目描述

给定两个有序数组 nums1 和 nums2,长度分别为 m,nm,n。请找出它们的中位数,要求时间复杂度在 O(log(n+m))O(log(n+m)) 以内。

样例1

nums1 = [1, 3]
nums2 = [2]

中位数是 2.0

样例2

nums1 = [1, 2]
nums2 = [3, 4]

中位数是 (2 + 3) / 2 = 2.5

思路

对于两个有序数组的中位数问题,可以考虑分别寻找两个数组的中位数,比较二者大小,例如当num1的中位数小于nums2的中位数时,因为我们知道两个数组总的中位数应该介于二者之间,那么nums1小于其中位数的一定不包含最终所求的中位数,可以舍弃一半的数组长度,而减半的nums1再次执行于nums2中位数比较的操作,直到二者有一个数组为空便可以得到最终结果

C++代码

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int total = nums1.size() + nums2.size();//计算两数组总长度
        if (total % 2 == 0)
        {
            int left = findKthNumber(nums1, 0, nums2, 0, total / 2);
            int right = findKthNumber(nums1, 0, nums2, 0, total / 2 + 1);
            return (left + right) / 2.0;//转浮点数
        }
        else return findKthNumber(nums1, 0, nums2, 0, total / 2 + 1);
    }
    int findKthNumber(vector<int> &nums1,int i,vector<int> &nums2,int j,int k){//分别代表传入的两个数组地址参数,开始比较的起点下标,返回第k大元素
        if(nums1.size()-i>nums2.size()-j) 
            return findKthNumber(nums2,j,nums1,i,k);//交换数组,使得短的在前,长的在后
        if(nums1.size()-i==0)//nums1为空
            return nums2[j+k-1];
        if(k==1) return min(nums1[i],nums2[j]);//k==1则寻找最小的元素
        int ni = min(i+k/2,int(nums1.size()));//k/2可能大于nums1的长度
        int nj=j+k/2;//nj较大,一定大于k/2
        if(nums1[ni-1]>nums2[nj-1])
            return findKthNumber(nums1,i,nums2,j+k/2,k-k/2);
            //nums2中位数更小,则他前面的都不是正确结果,可以舍掉
        else
            return findKthNumber(nums1,ni,nums2,j,k-(ni-i));
            //nums1更小,则他前面都不是,但是存在两种情况,起始位置更改为ni,减少的元素为ni左边的元素个数ni-i,因此寻找第k-(ni-i)个元素
    }  
};

递归

程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。


在这个问题中,找两个数组中位数的操作可以扩大化为寻找数组的第k大的元素,而这一操作贯穿整个程序,可以用递归来写,减少代码行数,增加可读性
这样问题变抽象成了在两个数组中寻找第k大的元素,根据中位数的定义,当数组数字为偶数个时,中位数为中间两个数的算术平均值,当长度为奇数个时,长度是最中间的数


在寻找最k大函数当中,为了简化情况讨论的次数,我们默认nums1的长度比nums2的长度小,当情况相反时,再次调用findKthNumber函数,将nums2传参给nums1,nums1传参给nums2,以修正为我们想要的默认情况,因为nums1的长度更短,我们只需要考虑nums1为空的情况,如果,Nums1为空,则中位数就是Nums2的中位数,而对于更一般的情况,对于取前k大元素,由于nums1长度更短,

[1]
[2,3,4,5,6]

例如在这样的情况中,k大于了nums1的长度,所以无法取到第k大,只能用最大的元素代替,在减少数组长度时,依然因为nums1长度更小,无法减少k/2的长度,只能减少到只保留最大值,删去其他元素的情况

遇到的坑

min函数只能比较两个相同数据类型的数,
int si = min(int(nums1.size()),i+k/2);

nums1.size()类型为std::vector::size_type,需要转为Int型才能比较

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int total = nums1.size()+nums2.size();
        if(total%2==0){
            int left = findKthNumber(nums1,0,nums2,0,total/2);
            int right = findKthNumber(nums1,0,nums2,0,total/2+1);
            return (left+right)/2.0;
        }
        else return findKthNumber(nums1,0,nums2,0,total/2+1);
    }
    int findKthNumber(vector<int> &nums1,int i,vector<int> &nums2,int j,int k){
        if(nums1.size()-i>nums2.size()-j) return findKthNumber(nums2,j,nums1,i,k);
        if(nums1.size()==i) return nums2[j+k-1];
        if(k==1) return min(nums1[i],nums2[j]);
        int si = min(int(nums1.size()),i+k/2);
        int sj = j+k/2;
        if(nums1[si-1]>nums2[sj-1])
            return findKthNumber(nums1,i,nums2,j+k/2,k-k/2);
        else
            return findKthNumber(nums1,si,nums2,j,k-(si-i));
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值