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

一、题目描述

在这里插入图片描述

二、解题思路

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

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

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

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

四、运行结果

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章