题目大意
给定两个有序数组,求这两个数组的中位数,时间复杂度要求是O(log(n+m))。
分析
这道题各种解法很多,我这里提供了关于中位数定义的那种解法。
简单来讲,中位数就是找到一组有序数中间位置的值,如果这组数有偶数个,那么中位数是最中间两个数的平均值,奇数个那么就是正中间的那个数。
把数组nums1和数组nums2分别在 i 和 j 进行划分,将 i 的左边和 j 的左边组合成“左半部分”,将 i 的右边和 j 的右边组合成“右半部分”。
1. 当 nums1数组和nums2数组的总长度是偶数时,如果能够保证
(1) 左半部分的长度等于右半部分,即:
(2) 左半部分最大值小于等于右半部分最小值
那么,中位数就可以表示如下
(左半部分最大值 + 右半部分最小值 )/ 2。
2. 当nums1数组和nums2数组的总长度是奇数时,如果能够保证
(1) 左半部分的长度比右半部分大1,即:
(2) 左半部分最大值小于等于右半部分最小值
那么,中位数就是左半部分最大值,也就是左半部比右半部分多出的那一个数:
上边的两种情况下的条件一其实可以合并为 j=(m+n+1)/2-i,因为如果m+n是偶数,由于取的是int值,所以加1也不会影响结果。另外在0<=i<=m时,为了保证0<=j<=n,我们必须保证 m <= n,原因可以通过不等式的简单放缩得到(这里可以理解为i是自变量,然后去控制函数 j 的值域):
最后一步由于是整数间的运算,所以 1/2=0。
而对于第二个条件,奇数和偶数的情况是一样的,进一步分析。
为了保证,
因为数组是有序的,所以nums1[i - 1] <= nums1[i],nums2[i - 1] <= nums2[i]这是天然的,所以我们只需要保证nums2[j - 1] <= nums1[ i ] 和 nums1[ i - 1 ] <= nums2[ j ] 所以我们分两种情况讨论:
nums2[j - 1] > nums1[ i ],并且为了不越界,要保证 j != 0,i != m。
此时很明显,需要增加 i ,为了数量的平衡还要减少 j ,而由于 j = ( m + n + 1) / 2 - i,i 增大,j 自然会减少。
nums1[i - 1] > nums2[j],并且为了不越界,要保证 i != 0,j != n
此时和上边的情况相反,我们要减少 i ,增大 j 。
接下来讨论上述二种情况的边界:
1. 当 i = 0, 或者 j = 0,也就是切在了最前边。此时左半部分当 j = 0 时,最大的值就是 nums1[ i - 1 ] ;当 i = 0 时 最大的值就是 nums2[ j - 1] 。右半部分最小值和之前一样。
2. 当 i = m 或者 j = n,也就是切在了最后边。此时左半部分最大值和之前一样。右半部分当 j = n 时,最小值就是 nums1[ i ] ;当 i = m 时,最小值就是nums2[ j ] 。
最后需要考虑枚举 i 的方式,二分搜索即可。
代码
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
if(m > n) {
return findMedianSortedArrays(nums2, nums1);
}
int iMin = 0, iMax = m;
while(iMin <= iMax) {
int i = (iMin + iMax) >> 1;
int j = ((m + n + 1) >> 1) - i;
if(j != 0 && i != m && nums2[j - 1] > nums1[i]) {
iMin = i + 1;
} else if(i != 0 && j != n && nums1[i - 1] > nums2[j]) {
iMax = i - 1;
} else {
int maxLeft = 0;
if(i == 0) {
maxLeft = nums2[j - 1];
} else if(j == 0) {
maxLeft = nums1[i - 1];
} else {
maxLeft = max(nums1[i - 1], nums2[j - 1]);
}
if((m + n) % 2 == 1) {
return maxLeft;
}
int minRight = 0;
if(i == m) {
minRight = nums2[j];
} else if(j == n) {
minRight = nums1[i];
} else {
minRight = min(nums1[i], nums2[j]);
}
return (minRight + maxLeft) / 2.0;
}
}
return 0.0;
}