一、题目描述
二、解题思路
题目要求时间复杂度为,很明显需要采用二分法来解决。
如果我们可以在这两个有序序列中找到第个数字,那么我们只要能找到中间的一个或两个数字,就一定可以得到这道题的解。
所以问题退化为寻找两个有序序列的按序第个数字。
在寻找中间的数字的过程中,需要区分以下两个序列长度和为奇数偶数的区别:
- 如果为奇数,那么中间的数字只有一个,那就是第个位置的元素
- 如果是偶数,那么中间的数字有两个,分别第是和个元素
- 其实,奇数时,与的值是相等的;那么就可以得出:
- 当奇数时,中位数就是按序排列后的第或个元素
- 当偶数时,中位数就是按序排列后的第或个元素的平均值
确定了结果所需数字的位置,便可开始根据这个位置在两个有序数组中找到这个位置的工作,这里采用一个辅助函数来完成。
- 如果起始位置大于等于数组长度,说明这个数组需要查找的部分已经变空,这个位置一定不可能在该数组里,那么直接返回另一个数组的第个位置
- 如果,那么就直接返回两个数组待查找部分两个开头元素的较小值
- 否则,对进行二分,需要比较两个数组里偏移待查起始位置处元素的大小,分别记为和
- 如果,说明第一个数组划分范围过大,导致其偏移处的元素过小,需要缩小第一个数组的范围到更大的部分,调整第一个数组的起始位置到后处
- 反之,需要调整第二个数组的起始位置到后处
- 调整的同时,还要缩小的范围,这里是把缩小到而不是,原因在于如果为偶数还好办,如果为奇数的话,比如说,就会直接变成,但实际上我们知道应该是变成,这样才满足二分的定义,否则就过不去测试用例
[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;
}
};