一、题目描述
二、解题思路
题目要求时间复杂度为
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
K−K/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;
}
};