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

给定两个正序数组nums1和nums2,找到它们的中位数。要求时间复杂度为O(log(m+n))。通过二分查找法实现,参考优快云上的博客,时间复杂度降低到O(min(logM, logN)),但实际执行时间可能并不显著减少。" 126488027,7435057,配置无线网络802.1X RADIUS认证,"['WLAN', '网络配置', '无线网络', '认证方案', 'RADIUS服务器']

描述

给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的中位数。

进阶:你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?

在这里插入图片描述
提示:

  • nums1.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -10^6 <= nums1[i], nums2[i] <= 10^6

思路1

  • 这道题难度为hard主要是因为有log(m+n)的时间复杂度要求,如果没有这个要求的话就很简单了。
  • 比如,直接合并两个数组,然后进行一次排序,就找到了中位数。但这样没有用到“两个数组有序”这个条件,且合并+排序操作会导致时间复杂度很高。
    在这里插入图片描述

解答1

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        nums1.insert(nums1.end(), nums2.begin(), nums2.end());
        sort(nums1.begin(), nums1.end());
        int size = nums1.size();
        double tmp;
        if(size%2 == 0){
            double a = (nums1[size/2-1] + nums1[size/2]);
            tmp = a/2;  
        }
        return (size % 2 == 0) ? tmp : nums1[size/2];
    }
};

思路2

前面做过一道合并两个有序数组的题,时间复杂度为O(m+n)。合并后的有序数组直接取中位数的值即可,但是还没到题目要求的log。

思路3:二分查找

  • 一般看到O(log)的时间复杂度,一般都是二分查找法。这个思路我没想到,直接看评论区和题解了。
  • 在优快云上找到了这篇博客:LeetCode Median of Two Sorted Arrays 在两个已排列的数组中找出中位数。时间复杂度为O(log(min(N,M)),可以参考作者的写法,和题解的做法是一样的。
  • 这样时间复杂度就降到了O(min(logM, logN)),其中M和N分别是数组1和数组2的长度。虽然时间复杂度大幅度降低,但让我奇怪的是执行时间相较于思路1并没有减少很多,难道是测试用例不够长?
    在这里插入图片描述

解答3

class Solution {
public:
int MAX_VALUE = 2147483647; //0x80000000;
int MIN_VALUE = -2147483648; //0x7fffffff;
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int size1 = nums1.size();
        int size2 = nums2.size();
        int size = size1 + size2;
        //交换数组,保证nums1是较短的数组,减少查找次数
        if(size1 > size2) return findMedianSortedArrays(nums2, nums1);
        //只有一个nums2数组,直接返回中位数即可
        if(size1 == 0)
            //涵盖了奇数个数和偶数个数的情况
            return ((double)nums2[(size2-1) / 2] + (double)nums2[size2 / 2]) / 2;
        int cutL = 0, cutR = size1;
        int cut1 = 0;//cut1保存当前在数组1的哪个位置前切一刀
        int cut2;//cut2保存当前在数组2.........
        while(cut1 < size1){
            cut1 = (cutR - cutL) / 2 + cutL;//从中间开始对数组1切第一刀,位置在第size1/2前
            cut2 = size / 2 - cut1;//那么数组2切的位置也是固定的

            double l1 = (cut1 == 0) ? MIN_VALUE : nums1[cut1 - 1];
            double l2 = (cut2 == 0) ? MIN_VALUE : nums2[cut2 - 1];
            double r1 = (cut1 == size1) ? MAX_VALUE : nums1[cut1];
            double r2 = (cut2 == size2) ? MAX_VALUE : nums2[cut2];
            
            if(l1 > r2) cutR = cut1 - 1;
            else if(l2 > r1) cutL = cut1 + 1;
            else {//找到正确位置
                if(size % 2 == 0){//一共偶数个值
                    double leftMax = (l1 > l2) ? l1 : l2;//左边较大的值
                    double rightMin = (r1 > r2) ? r2 : r1;//右边较小的值
                    return (leftMax + rightMin) / 2;
                }else{//奇数个,切完后左边总数比右边少一个,中位数必定出现在右边
                    return (r1 < r2) ? r1 : r2;
                }
            }
        }
        return 0;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值