题目
思路
如下图所示,中位数必然满足这样的分割线, 将两个数组分别分为两个部分,最终组成四个部分,分别为
t1_left, t1_right, t2_left, t2_right
, 其中t1代表第一个数组, t2代表第二个数组, left 代表分割线左边的部分, right 代表分割线右边的部分。
- 要解决的问题就是找到这样一个问题 左边的元素个数为
(m+n+1)/2
, 用i
表示分割线在t1
中的位置,j
代表分割线在t2
中的位置。- 那么必须满足
t1[i-1]<=t2[j] && t2[j-1] <= t1[i] && i+j=(m+n+1)/2
;- 由于
i+j=(m+n+1)/2
; 所以只要二分查找出第一个数组中i
的位置,自然确定j
的位置,满足i
位置满足条件t1[i-1]<=t2[j] && t2[j-1] <= t1[i]
即可。
当 m+n
是奇数时,只需要找到中间位置的元素,max(t1_left, t2_left)
当 m+n
是偶数时,需要找到中间俩元素,取平均值 (max(t1_left, t2_left) + min(t1_right+t2_right))/2
值得注意的是,由于是分割线,m个元素的数组有m+1个分割位置,即将所有元素分为right部分 或者 所有元素分为left部分,此时需要边界值处理。
即代码中的如下
double t1_left = (i==0 ? -DBL_MAX : t1[i-1]), t1_right = (i==m ? DBL_MAX : t1[i]) , t2_left = (j == 0 ? -DBL_MAX : t2[j-1]), t2_right = (j == n ? DBL_MAX : t2[j]);
代码
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
vector<int> t1 = nums1, t2 = nums2;
if ( t1.size() > t2.size() ) {
vector<int> temp = t1;
t1 = t2;
t2 = temp;
}
int m = t1.size(), n = t2.size();
int left = 0, right = m, mid = (m+n+1)/2;
// 找到一条分割线 使得 t1[i-1] <= t2[j] && t2[j-1] <= t1[i] 且 i+j = (m+n+1)/2 分割线的左侧为
int i = 0, j = 0;
// 二分查找
while (left < right) {
i = left + (right - left + 1) / 2;
j = mid - i;
if (t1[i-1] > t2[j]) {
right = i-1;
} else {
left = i; // 包含了t1[i-1]==t2[j]的情况 所以不能用left=i+1,而使用left=i,可能导致死循环,通过上述求i,即中位数时 +1
}
}
i = left;
j = mid - left;
// cout << i << " " << j;
double t1_left = (i==0 ? -DBL_MAX : t1[i-1]), t1_right = (i==m ? DBL_MAX : t1[i]) , t2_left = (j == 0 ? -DBL_MAX : t2[j-1]), t2_right = (j == n ? DBL_MAX : t2[j]);
// cout << t1_left << " " << t1_right << " " << t2_left << " " << t2_right;
if ( (m+n) % 2 == 1) {
// cout << t1_left << " " << t2_left << " " << DBL_MIN << " " << DBL_MAX;
return max(t1_left, t2_left);
} else {
return ( max(t1_left, t2_left) + min(t1_right, t2_right) )/2.0;
}
return 0.0;
}
};