4. Median of Two Sorted Arrays
Leetcode Array Binary Search Divide and ConquerThere 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
分析¶
寻找两个有序数组的中位数。最直接的方法就是把两个数组合并、排序,求出中位数。时间复杂度是O((m+n)\log (m+n),空间复杂度是O(m+n).
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int n = nums1.length, m = nums2.length;
int[] nums = new int[n + m];
System.arraycopy(nums1, 0, nums, 0, n);
System.arraycopy(nums2, 0, nums, n, m);
Arrays.sort(nums);
int mid = (n + m) / 2;
if ((n + m) % 2 == 1) return (double) nums[mid];
else return ((double) (nums[mid]) + nums[mid - 1]) / 2;
}
在合并的过程中,注意到两个数组分别有序,所以很容易联想到归并排序的合并过程。合并结束以后,数组是排序状态。时间复杂度降为O(n+m)。
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int n = nums1.length, m = nums2.length;
int[] nums = new int[n + m];
merge(nums1, nums2, nums);
int mid = (n + m) / 2;
if ((n + m) % 2 == 1) return (double) nums[mid];
else return ((double) (nums[mid]) + nums[mid - 1]) / 2;
}
private void merge(int[] nums1, int[] nums2, int[] merged) {
int n = nums1.length, m = nums2.length, q = merged.length;
int l = 0, r = 0, k = 0;
while (k < q) {
if (l == n) merged[k++] = nums2[r++];
else if (r == m) merged[k++] = nums1[l++];
else if (nums1[l] < nums2[r]) merged[k++] = nums1[l++];
else merged[k++] = nums2[r++];
}
}
但是以上方法都不符合题目的时间复杂度要求O(\log (m+n))。假设i,j把数组A,B分别分成左右两部分,左边部分的长度等于右边长度:
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]
如果可以保证
len(left_part) = len(right_part)
max(left_part) <= min(right_part)
也就是说把数组A、B分成相等长度的两部分,左边部分中的所有数字都小于右半部分的所有数字。那么可以求出中位数
\small \text{median} = \frac{\max(\text{left_part})+\min(\text{right_part})}{2}
所以题目就转化成
在[0, m]中寻找i,使得B[j-1] \le A[i]并且A[i-1]\leB[j],其中j=\frac{m+n+1}{2}-i
public double findMedianSortedArrays(int[] A, int[] B) {
int m = A.length;
int n = B.length;
if (m > n) { //保证m<=n
int[] temp = A; A = B; B = temp;
int tmp = m; m = n; n = tmp;
}
//i的取值范围是iMin~iMax, halfLen是A+B的一半长度,向上取整
int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
int i = 0, j = 0;
// 类似于二分查找
while (iMin <= iMax) {
i = (iMin + iMax) / 2;
j = halfLen - i;
if (i < iMax && B[j-1] > A[i])
iMin = i + 1; // i is too small
else if (i > iMin && A[i-1] > B[j])
iMax = i - 1; // i is too big
else break; // i is perfect
}
// 寻找左值
int maxLeft = 0;
if (i == 0) maxLeft = B[j-1]; // left-A是空的
else if (j == 0) maxLeft = A[i-1]; // left-B是空的
else maxLeft = Math.max(A[i-1], B[j-1]);// A, B取最大
if ( (m + n) % 2 == 1 ) return maxLeft; // 奇数长度,中位数就1个
// 寻找右值
int minRight = 0;
if (i == m) minRight = B[j];
else if (j == n) minRight = A[i];
else minRight = Math.min(B[j], A[i]);
return (maxLeft + minRight) / 2.0;
}