4. Median of Two Sorted Arrays
Question
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
Solution
该问题的主要难点在于O(log(m+n))的时间复杂度限制,这意味着无法将2个有序数组合并排序之后再获取中位数。
解法:在不合并2个有序数组的情况下,使用Binary Search找出中位数。
Binary Search似乎很难在2个数组上找出中位数,故需要进一步挖掘中位数的性质:
- 对于长度为偶数E的有序数组,将其从中间分为2个长度为E/2的子数组Left与Right,
Median = (MaxLeft+MinRight)/2 - 对于长度为奇数O的有序数组,将其从中间分为长度为(O+1)/2的子数组Left和长度为(O-1)/2的子数组Right,
Median = MaxLeft - MaxLeft是左子数组(也是较小值构成的数组)中的最大值(也是最右端的值);MinLeft是右子数组(较大值构成的数组)中的最小值(也是最左端的值)
对于作为输入的2个有序数组A, B来说,合并数组的左子数组一定由A的前i个元素和B的前j个元素构成。因为他们代表了输入中的较小部分元素。确定了i, j的值就可以得到需要的中位数。
推理过程:
MaxLeft <= MinRight; MaxLeft = max(A[i-1], B[j-1]); MinRight = min(A[i], B[j]); i+j = int((m+n+1)/2)
等价于:max(A[i-1], B[j-1]) <= min(A[i], B[j]); i+j = int((m+n+1)/2)
等价于:
A[i-1]<=A[i];
A[i-1]<=B[j]; (1)
B[j-1]<=B[j];
B[j-1]<=A[i]; (2)
i+j=int((m+n+1)/2); (3)
其中除(1) (2) (3)式之外的2个不等式在题设条件(A, B是有序数组)下是恒成立的,可以省略。
那么本题就相当于使用Binary Search解决下列的一个规划问题,得到一组必定存在的i, j
Limitation:
- A[i-1]<=B[j]; (1)
- B[j-1]<=A[i]; (2)
- i+j=int((m+n+1)/2); (3)
为什么要说“规划问题”?因为如果把A, B看做两个离散空间上的函数,与中学数学中的线性规划问题很相似,区别就是线性规划用推理方式寻找解的区域,该问题用搜索方法求解。
由于(3)等式规定了i, j的恒定关系,故只有i一个变量。找到满足(1)(2)式的i即解决该问题。
对于给定的i,(1)(2)共有四种成立状态的组合:
- (1)(2)均成立。很显然这就找到满足要求的i了。
- (1)成立 (2)不成立。i需要增大,增大的方式当然不是简单的加一,而是要利用Binary Search咯。
- (1)不成立 (2)成立。i需要减小,同理。
- (1)不成立 (2)不成立。利用A, B有序数组的性质可以证明矛盾,该情况不可能存在。
实现注意要点:
- corner cases,i增大减小都需要验证一下是否有数组空间做这个操作。如果遇到(1)(2)无法满足,数组空间又不足怎么办?由于这个问题必定有解,那肯定就是搜索下界等于搜索上界,i也随之确定。
- 空数组输入问题。“You may assume nums1 and nums2 cannot be both empty.”意味着可能包含一个空数组。
- 为什么要先假设m<n?因为
j=(m+n+1)/2-i,而0<= i <=m,如果m较大那么i的实际可取值范围应该在0到一个比m小的正数之间,从而才能使j始终为正数。为了使算法简洁,不如假设m<n,如果不符合就交换2个参数。
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size();
int n = nums2.size();
if (m>n) {
return findMedianSortedArrays(nums2, nums1);
}
// iMin, iMax确定当前Binary Search的范围
// 注意
int iMin=0, iMax=m, leftLen = (m+n+1)/2;
while(iMin<=iMax) {
int i = (iMin+iMax)/2;
int j = leftLen - i;
if (i<iMax && nums2[j-1]>nums1[i]) {
iMin = i+1;
} else if (i>iMin && nums1[i-1]>nums2[j]) {
iMax = i-1;
} else {
int maxLeft = 0, minRight = 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]);
// 及时返回,防止nums1为空数组的时候访问nums1
if ((m+n)%2==1) return maxLeft;
if (i==m) minRight = nums2[j];
else if (j==n) minRight = nums1[i];
else minRight = min(nums1[i], nums2[j]);
return (maxLeft+minRight)/2.0;
}
}
return 0.0;
}
};