Problem
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)).
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
寻找2个有序数组的中位数,假设数组A和数组B的长度分别为m和n,最简单的办法是用两个指针分别指向2个数组的头,依次比较,将数值更小的数组指针向前移动,比较(m+n+1)/2次后即可得到结果,这一算法的时间复杂度是O(m+n),在答案区看到了一个更巧妙的解法。
left_part | right_part
A[0],A[1],...,A[i-1] | A[i],A[i+1],...A[m-1]
B[0],B[1],...,B[j-1] | B[j],B[j+1],...B[n-1]
我们总使得A的长度m小于等于B的长度n,因此如果刚开始给的A和B长度不满足m<=n,则用B替代A,A替代B。如果找到A数组和B数组的合理分隔点,即可以得到两个数组的中位数。假设A和B的分隔点分别是i和j,则满足以下2个条件:
① left_part和right_part的长度相同或差1
如果m+n是偶数,则
i + j = m - i + n - j
如果m+n是奇数,则(这种情况下我们使左边比右边多1)
i + j = m - i + n - j + 1
综合2种情况, j = (m + n + 1)/2 - i
② left_part的数全部不大于right_part的数,即max(left_part) <= min(right_part)
B[j-1] <= A[i] and A[i-1] <= B[j]
现在问题转化为:i从0到m中,找到一个符合条件的i使得B[j-1] <= A[i] and A[i-1] <= B[j], j = (m + n + 1)/2 - i,很显然在排序数组中可以用二分查找法
imin = 0, imax = m
i = (imin + imax)/2, j = (m + n + 1)/2 - i
while (imin <= imax) {
①if (B[j-1] <= A[i] and A[i-1] <= B[j])
找到了i
②if (B[j - 1] > A[i])
说明i找的过小,imin = i + 1
③if (A[i - 1] > B[j])
说明i找的过大,imax = i - 1
}
边界条件说明:
i = 0, i = m, j = 0, j = n是边界情况,在二分查找过程中要做一定限制。
在情况②中,设定i < m ==> j = (m+n+1)/2 - i > (m+n+1)/2 - m >= (2*m+1)/2 - m >= 0
在情况③中,设定i > 0 ==> j = (m+n+1)/2 - i < (m+n+1)/2 <= (2*n+1)/2 <= n
综上,这种解法的时间复杂度是O(log(min(m,n)))。
public class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
if(m>n) return findMedianSortedArrays(nums2,nums1);
//for all the following, we assumed m<=n;
int imin = 0;
int imax = m;
int max_of_left;
int min_of_right;
int i=(imin+imax)/2, j=(m+n+1)/2 -i;
while(imin <= imax){
i=(imin+imax)/2;
j = (m+n+1)/2 -i;
if(i>0 && nums1[i-1] > nums2[j]){
imax = i-1;
}else if(i < m && nums2[j-1] > nums1[i]){
imin = i+1;
}else break;
}
if(i == 0) max_of_left = nums2[j-1];
else if(j == 0) max_of_left = nums1[i-1];
else max_of_left = Math.max(nums1[i-1],nums2[j-1]);
if((m + n) % 2 == 1) return max_of_left;
if(i == m) min_of_right = nums2[j];
else if( j == n) min_of_right = nums1[i];
else min_of_right = Math.min(nums1[i],nums2[j]);
return (max_of_left+min_of_right)/2.0;
}
}