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

一、题目描述

在这里插入图片描述

二、解题思路

  题目要求时间复杂度为 O ( l o g ( m + n ) ) O(log(m + n)) O(log(m+n)),很明显需要采用二分法来解决。
  如果我们可以在这两个有序序列中找到第 K K K个数字,那么我们只要能找到中间的一个或两个数字,就一定可以得到这道题的解。
  所以问题退化为寻找两个有序序列的按序第 K K K个数字。
  在寻找中间的数字的过程中,需要区分以下两个序列长度和为奇数偶数的区别:

  • 如果为奇数,那么中间的数字只有一个,那就是第 ( l e n 1 + l e n 2 + 1 ) / 2 (len1 + len 2 + 1) / 2 (len1+len2+1)/2个位置的元素
  • 如果是偶数,那么中间的数字有两个,分别第是 ( l e n 1 + l e n 2 + 1 ) / 2 (len1 + len2 + 1) / 2 (len1+len2+1)/2 ( l e n 1 + l e n 2 ) / 2 + 1 (len1 + len2) / 2 + 1 (len1+len2)/2+1个元素
  • 其实,奇数时, ( l e n 1 + l e n 2 + 1 ) / 2 (len1 + len 2 + 1) / 2 (len1+len2+1)/2 ( l e n 1 + l e n 2 + 2 ) / 2 (len1 + len2 + 2) / 2 (len1+len2+2)/2的值是相等的;那么就可以得出:
    • 当奇数时,中位数就是按序排列后的第 ( l e n 1 + l e n 2 + 1 ) / 2 (len1 + len 2 + 1) / 2 (len1+len2+1)/2 ( l e n 1 + l e n 2 + 2 ) / 2 (len1 + len 2 + 2) / 2 (len1+len2+2)/2个元素
    • 当偶数时,中位数就是按序排列后的第 ( l e n 1 + l e n 2 + 1 ) / 2 (len1 + len 2 + 1) / 2 (len1+len2+1)/2 ( l e n 1 + l e n 2 + 2 ) / 2 (len1 + len 2 + 2) / 2 (len1+len2+2)/2个元素的平均值

  确定了结果所需数字的位置,便可开始根据这个位置在两个有序数组中找到这个位置的工作,这里采用一个辅助函数来完成。

  • 如果起始位置大于等于数组长度,说明这个数组需要查找的部分已经变空,这个位置一定不可能在该数组里,那么直接返回另一个数组的第 K K K个位置
  • 如果 K = = 1 K == 1 K==1,那么就直接返回两个数组待查找部分两个开头元素的较小值
  • 否则,对 K K K进行二分,需要比较两个数组里偏移待查起始位置 K / 2 K / 2 K/2处元素的大小,分别记为 h a l f 1 half1 half1 h a l f 2 half2 half2
    • 如果 h a l f 1 < h a l f 2 half1 < half2 half1<half2,说明第一个数组划分范围过大,导致其偏移 K / 2 K / 2 K/2处的元素过小,需要缩小第一个数组的范围到更大的部分,调整第一个数组的起始位置到后 K / 2 K / 2 K/2
    • 反之,需要调整第二个数组的起始位置到后 K / 2 K / 2 K/2
    • 调整的同时,还要缩小 K K K的范围,这里是把 K K K缩小到 K − K / 2 K - K / 2 KK/2而不是 K / 2 K / 2 K/2,原因在于如果 K K K为偶数还好办,如果为奇数的话,比如说 5 5 5,就会直接变成 K = 2 K = 2 K=2,但实际上我们知道应该是变成 K = 3 K = 3 K=3,这样才满足二分的定义,否则就过不去测试用例[1, 2]; [3, 4]

三、解题代码

class Solution {
private:
    int findKth(vector<int>& num1, int start1, vector<int>& num2, int start2, int k){
        if(start1 >= num1.size())   return num2[start2 + k - 1];
        if(start2 >= num2.size())   return num1[start1 + k - 1];
        if(k == 1)  return min(num1[start1], num2[start2]);
        int half1 = (start1 + k / 2 - 1) < num1.size() ? num1[start1 + k / 2 - 1] : INT_MAX;
        int half2 = (start2 + k / 2 - 1) < num2.size() ? num2[start2 + k / 2 - 1] : INT_MAX;
        return half1 < half2 ? findKth(num1, start1 + k / 2, num2, start2, k - k / 2) : findKth(num1, start1, num2, start2 + k / 2, k - k / 2);
    }
public:
    double findMedianSortedArrays(vector<int>& num1, vector<int>& num2) {
        int len1 = num1.size();
        int len2 = num2.size();
        int left = (len1 + len2 + 1) / 2;
        int right = (len1 + len2 + 2) / 2;
        return (findKth(num1, 0, num2, 0, left) + findKth(num1, 0, num2, 0, right)) / 2.0;
    }
};

四、运行结果

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值