给出两个有序数组nums1, nums2, 求 nums1 + nums2的中位数, 并且时间复杂度要求0(log(n+m))
思路:
时间复杂度log,并且数组有序, 所以进而可以想到用二分的方法。
由于数组有序。那么假设我们将数组合并排序,那么中位数 应该是[(n+m-1)/2 + (n+m)/2]/2 或者 (n+m)/2
那么也就是说 假设第一个数组 取 k 个数,那么第二个数组 应该取 mid -k
example
nums1 1 3 5 6 8
nums2 2 3 5
如果 mid 应该是merge 后的 第 3 位 与第 4 位 /2( 1 2 3 3 5 5 6 8)
那么假设 前面 mid - 1个数字, 我有k位是从 nums1 中获取的,那么就会有 mid - k - 1位从 nums2 中获取的。那么 mid = [min(nums1[k+1], nusm2[mid-k]) + max(nums1[k], nums2[mid-k-1])]/2; 那么也就是说我要考虑在nums1中切一刀(k+1与k之间),把nums1切两半,那么同样,nums被切割的位置也应运而生(mid-k-1 与 mid-k)之间。
那么其实问题就演变成。怎样切割nums1,使得切割的左半部分无完全小于有右半边(包括nums1 与nums2)
考虑
也就是 最终的跳出条件应该是 ai < bj+1 && bj < ai+1
并且最终的答案就是[ max(ai + bj) + min(bj+1, ai+1) ] / 2
如果 ai > bj+1 , 那么就是 nums1 切的太靠后了,要往前一点,可以用二分。 high = mid - 1;
如果 ai+1 < bj 那么就是nums2 切的太靠前了,要往后一点。low = mid+1
那如果nums1 到头了呢,考虑,由于我们在产生最终答案的时候,最左边的数字一定是取得max,最右边的数组一定是取min,那么为了排除边界的干扰,我们可以把它设置成一个取不到的值,INT_MIN INT_MAX
但是这样就要求我们的 nums1 是 短于nums2 的,这样的话可以确保第一次 切割不回导致nums2 切到头,否则int_min int_max就会影响结果。
以上考虑的是n+m偶数的情况,那么n+m是奇数可能稍微复杂一些,那么我们能不能考虑在不影响结果的情况下,把偶数序列演变成偶数数序列呢。
也就是有什么办法可以使得, f(n) /2 = f(n+1)/2 并且f(n) 恒为奇数
这个时候我们就可以联想到 最长回文子串的manacher算法
[1 4 7 9] [# 1 # 4 # 7 # 9 #] len : 4->9
[2 3 5] [# 2 # 3 # 5 #] len: 3->7
而且个位置可以通过/2得到原来元素的位置。
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int len1 = nums1.size();
int len2 = nums2.size();
if (len1 > len2)
return findMedianSortedArrays(nums2, nums1);
int low = 0, high = len1*2;
int mid1 = 0, mid2 = 0;
int l1, l2, r1, r2 = 0;
while(low <= high) {
mid1 = (low + high) >> 1;
mid2 = len1 + len2 - mid1;
l1 = (mid1 == 0) ? INT_MIN : nums1[(mid1-1)/2];
r1 = (mid1 == 2*len1) ? INT_MAX : nums1[mid1/2];
l2 = (mid2 == 0)? INT_MIN : nums2[(mid2-1)/2];
r2 = (mid2 == 2*len2) ? INT_MAX : nums2[mid2/2];
if(l1 > r2)
high = mid1 - 1;
else if (l2 > r1)
low = mid1 + 1;
else
break;
}
return (double) (max(l1,l2) + min(r1, r2)) / 2;
}
};