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
题目描述:给定两个有序的整型数组,要求在时间复杂度O(log (m+n))的条件下,找出两个数组的中位数。其中m和n分别为两个数组的长度。
思路分析:这里暂设数组nums1的长度为m,数组nums2的长度为n。
解题之前,先要理解一下中位数概念的由来。中位数可以将原来的集合分成两个大小相等的子集,使得一个子集中的任一个数都大于另一个子集中的任一个数。
在本题中,可以考虑将数组A分成 A[0], A[1], ..., A[i-1] 和 A[i], A[i+1], ..., A[m-1] 左右两个部分,将数组B分成 B[0], B[1], ..., B[j-1] 和 B[j], B[j+1], ..., B[n-1] 左右两个部分。这样的话,只要使得 A左+B左中数的数量与A右+B右中数的数量相等,且满足左边的最大值小于等于右边的最小值,即可求出这两个数组的中位数 = (左边最大值+右边最小值) / 2。
将上面的两个条件形式化表示,即为:
1) i + j == m + n + 1 - i - j (或:m+n-i-j )
2) A[i]>=B[j-1] && A[i-1]<=B[j]
为了讨论方便,暂取 n >= m,这样就有 j = (m+n+1)/2 - i,且 j 的值不会为负。
ps:这里的 i 和 j 的值分别可以取到 0, m 和 0, n 是为边界值,关于这个的处理比较简单:若 i 或 j == 0 ,则表示左边的最大值就是 B[j-1] 或 A [i-1];如果 i == m 或者 j == n ,则右边的最小值即为 B[j] 或 A[i]。
具体的算法设计:在 [0,m] 区间上二分搜索符合条件的 i (看到题目中要求的 log 量级的时间复杂度,应当可以条件反射地想到二分搜索)。
<1> 设置变量 left = 0, right = m
<2> 令 i = (left + right) / 2, j = (m+n+1)/2 - i
<3> (通过第二步,我们已经使得 左边整数数量==右边整数数量) 那么,下面有三种可能的情况:
a) A[i] >= B[j-1] && A[i-1] <= B[j] : 此时即找到了符合条件的 i ,可以停止查找,并计算中位数的值了;
b) A[i] < B[j-1] :此时的 i 太小了,需要增大 i 值,令 left = i + 1。跳回第二步;
c) A[i-1] > B[j] :此时的 i 太大了,需要减小 i 值,令 right = i - 1。跳回第二步;
ps:因为我们这里计算 j ,半长设的是 m + n + 1,如果 m + n 为奇数,那么此时的中位数就是左边的最大值。
AC的Java代码如下所示:
public class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
if (m>n)
return binSearch(nums2, nums1, n, m);
else
return binSearch(nums1, nums2, m, n);
}
public double binSearch(int[] a, int[] b, int m, int n) {
int left = 0, right = m, halfLen = (m+n+1)/2;
int maxLeft=0, minRight=0;
// binary search
while (left<=right) {
int i = (left+right)/2;
int j = halfLen - i;
if (i>0 && a[i-1]>b[j]) {
// the value of i is too large
right = i-1;
} else if (i<m && a[i]<b[j-1]) {
// the value of i is too small
left = i+1;
} else {
if (i==0)
maxLeft = b[j-1];
else if (j==0)
maxLeft = a[i-1];
else
maxLeft = Math.max(a[i-1], b[j-1]);
if ((m+n)%2 == 1)
return maxLeft;
if (i==m)
minRight = b[j];
else if (j==n)
minRight = a[i];
else minRight = Math.min(a[i], b[j]);
break;
}
}
return (maxLeft+minRight)/(2.0);
}
}