两个有序数组的中位数
题目描述
Median of Two Sorted Arrays
There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
You may assume nums1 and nums2 cannot be both empty.
Example 1:
nums1 = [1, 3] nums2 = [2] The median is 2.0
Example 2:
nums1 = [1, 2] nums2 = [3, 4] The median is (2 + 3)/2 = 2.5
题目分析
从时间复杂度反推可能使用的算法
本题要求时间复杂度在 O ( l o g ( m + n ) ) O(log(m+n)) O(log(m+n))以下,明显是二分查找的时间复杂度,所以考虑使用二分查找。
将问题抽象一下
对于两个数组A
,B
,求其中位数,等价于将A
,B
划分成左右两部分,这两部分满足
A[i - 1] <= B[j]
且B[j - 1] <= A[i]
(数组本身是升序,满足此公式表明完全左半部分全都小于右半部分)i + j = m - i + n - j
或i + j = m - i + n - j + 1
(依据m+n
的奇偶,奇数情况下无法半分)
上述公式中的i
和j
分别是A、B的划分点,如果划分点i
和j
满足上述两种情况,则可以推出中位数是
- 若
m + n
为奇数,则中位数是max(A[i - 1], B[j - 1])
- 若
m + n
为偶数,则中位数是(max(A[i - 1], B[j - 1]) + min(A[i], B[j]))/2
(即左半部分的最大值和右半部分的最小值的平均数)
因此,为了求出满足两件的i
和j
,针对特定的i
,可以推出j = (m + n + 1) / 2 - i
(注意这里除法是向下取整,因此无论m + n
是奇是偶j
均可依此公式算出,但是为了防止j
为负值,需要保证m <= n
),因此可二分查找满足条件的i
, 在二分查找过程中有以下三种情况,
A[i - 1] <= B[j]
且B[j - 1] <= A[i]
,此时i
即为所求A[i - 1] > B[j]
, 此时i
取值过大,导致A
的左半部大于B
的右半部,应减少i
B[j - 1] > A[i]
, 此时i
取值过小,导致B
的左半部大于A
的右半部,应增加i
上述三种情况均未考虑边界条件,即当i = 0, j = 0, i = m, j = n
这些特殊值时,对应A[i - 1], B[i - 1], A[i], B[j]
并不存在,其实这种情况很简单,因为如果A[i - 1], B[i - 1], A[i], B[j]
这些值存在,则需要满足上述公式,但是,若这些值并不存在,则并不需要满足上述公式。公式的表面含义是使得左半部分小于右半部分,如果没有则必然成立。所以,考虑边界情况时,
A[i - 1] <= B[j] || i = 0 || j = n
且B[j - 1] <= A[i] || j = 0 || i = m
,此时i
即为所求A[i - 1] > B[j] && i > 0
, 此时i
取值过大,导致A
的左半部大于B
的右半部,应减少i
B[j - 1] > A[i] && i < m
, 此时i
取值过小,导致B
的左半部大于A
的右半部,应增加i
对i
的增加和减少采用二分记得达到O(log(m+n))的时间复杂度。同理,若求得的i
是边界情况,即i = 0
或者i == m
,则对中位数的计算中需要考虑对应的值是否存在(详情见源代码)
源代码
C++实现如下:
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
/* 如果 m > n, 则交换nums1和nums2, 同时交换m和n */
if (m > n) {
nums1.swap(nums2);
int tl;
int t = m;
m = n;
n = t;
}
/* 二分查找过程 */
int mini = 0, maxi = m;
while (mini <= maxi) {
int i = (mini + maxi) / 2;
int j = (n + m + 1) / 2 - i;
if (i < m && nums2[j - 1] > nums1[i]) {
mini = i + 1;
} else if (i > 0 && nums1[i - 1] > nums2[j]) {
maxi = i - 1;
} else {
double maxl = 0;
if (i == 0) {
maxl = nums2[j - 1];
} else if (j == 0) {
maxl = nums1[i - 1];
} else {
maxl = max(nums1[i - 1], nums2[j - 1]);
}
if ((m + n) % 2 == 1) {
return maxl;
}
double minr = 0;
if (i == m) {
minr = nums2[j];
} else if (j == n) {
minr = nums1[i];
} else {
minr= min(nums1[i], nums2[j]);
}
return (maxl + minr) / 2.0;
}
}
return -1;
}
};