From: https://leetcode.com/problems/median-of-two-sorted-arrays/#/solutions
我的个人博客:http://riot-qiu.coding.me/2017/06/08/median-of-two-sorted-arrays
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
Binary Search、Array、 Divide and Conquer
为了解决这个问题,我们首先要明白中位数的作用。在统计学中,中位数能够将一组数据划分为两个长度相同的数据集,其中一个子集总是大于另外一个子集。 如果我们能够理解在划分上的作用,那么我们离正确答案已经非常接近了。
首先我们用一个随机位置i将A划分成两个部分:
left_A | right_A
A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]
假设A有m个元素,那么 i = 0 ~ m
。然后我们可以得到以下信息:
left_A.length = i, right_A.length = m - i;
PS:当i==0
, left_A 是空的,但i==m
, right_A 是空的。
同样我们用一个随机位置j将B划分成两部分:
left_B | right_B
B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
把left_A
和left_B
放在同一集合,把right_A
和right_B
放在另外一个集合,命名为left_part
和right_part
:
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]
如果我们能确定下面两个条件:
1) len(left_part) == len(right_part)
2) max(left_part) <= min(right_part)
那么我们就已经把{A,B}这个集合分成了两个相同长度的部分,而且右边始终大于左边。那么中位数就等于median = (max(left_part) + min(right_part))/2
.
PS:左边最大和右边最小之和的一半
为了保证左右数量两边相等和左边最大小于等于右边最小,我们就需要保证:
(1) i + j == m - i + n - j (or: m - i + n - j + 1)
if n >= m, 我们只需要确定i和j的关系: i = 0 ~ m, j = (m + n + 1)/2 - i
(2) B[j-1] <= A[i] and A[i-1] <= B[j]
说明:
1. 为了过程简单,我们先假设A[i-1],B[j-1],A[i],B[j]
这几个是一直有效的。(后面会说明怎么处理这些边界情况)
2. 为什么要n>=m
呢?因为这是为了保证在i
在大于等于0小于等于m的取值中,一直保持有效。因为j=(m+n+1)/2-i
,如果i=m
且n
j<0
,这会得到错误的答案。
i在[0,m]中取值,找到满足B[j-1] <= A[i] and A[i-1] <= B[j]
的情况,这个时候的i就是中间的位置。
imin = 0, imax = 0
, 然后在[imin, imax]
这个范围里面找i = (imin + imax)/2, j = (m + n + 1)/2 - i
B[j-1] <= A[i] and A[i-1] <= B[j]
,意味当前ij已经就是我们要找的位置,停止查找。B[j-1] > A[i]
,那么意味A[i]太小了,我们需要调整i让条件B[j-1] <= A[i]
满足。 B[j-1] <= A[i]
可能刚好能够满足。B[j-1] <= A[i]
依旧无法满足。[i+1, imax]
,即设置imin = i+1
,然后回到第二步。A[i-1] > B[j]
,那么意味A[i-1]
过大,与前一个条件相反,我们需要减少i来满足条件A[i-1]<=B[j]
,就是调整当前的搜索范围到[imin, i-1]
,即设置imax = i-1
,然后回到第二步处理。现在我们来考虑边界问题,当i和j等于i=0,i=m,j=0,j=n
这四种情况的时候,A[i-1],B[j-1],A[i],B[j]
这四个位置的值也有可能不存在。实际上,这个几种情况的处理比你想象中的要简单。
正常情况下,只要i和j不是边界,A[i-1],B[j-1],A[i],B[j]
这四个值必定存在,我们只需要确定条件B[j-1] <= A[i]
和条件A[i-1] <= B[j]
。当存在A[i-1],B[j-1],A[i],B[j]
没有值的情况,我们可能不需要确定两个条件。比如:当i=0
,A[i-1]
不存在,那么我们就不需要确定条件A[i-1] <= B[j]
.
所以判断的条件就变成:
B[j-1] <= A[i] && A[i-1] <= B[j] ===>
(j == 0 || i == m || B[j-1] <= A[i]) &&
(i == 0 || j == n || A[i-1] <= B[j])
而之前的循环需要判断的三个条件就变成:
1. 如果满足 (j == 0 || i == m || B[j-1] <= A[i]) && (i == 0 || j == n || A[i-1] <= B[j])
那么当前i就是要找的位置。
2. 如果 j > 0 and i < m and B[j - 1] > A[i]
,那么当前i太小。
3. 如果i > 0 and j < n and A[i - 1] > B[j]
, 那么当前的i太大。
因为m<=n,根据i的取值范围定义,我们可以得出下面结论,所以<2>、<3>中的j>0, j
m <= n, i < m ==> j = (m+n+1)/2 - i > (m+n+1)/2 - m >= (2*m+1)/2 - m >= 0
m <= n, i > 0 ==> j = (m+n+1)/2 - i < (m+n+1)/2 <= (2*n+1)/2 <= n
i < m ==> j > 0;
i > 0 ==> j < n;
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
if (m > n) {
m = n;
n = nums1.length;
int[] tmp = nums1;
nums1 = nums2;
nums2 = tmp;
}
int imin =0, imax=m,i,j,halfLen=(m+n+1)/2;
while (imin <= imax) {
i = (imin+imax)/2;
j = halfLen - i;
if (i < m && nums2[j-1] > nums1[i]) {
imin = i + 1;
} else if(i > 0 && nums1[i-1] > nums2[j]) {
imax = i - 1;
} else {
int left_max;
if (j==0) {
left_max = nums1[i-1];
} else if (i ==0 ) {
left_max = nums2[j-1];
} else {
left_max = Math.max(nums1[i-1],nums2[j-1]);
}
if ((m+n)%2 == 1) {
return left_max;
}
int right_min;
if (i == m) {
right_min = nums2[j];
} else if ( j == n) {
right_min = nums1[i];
} else {
right_min = Math.min(nums1[i],nums2[j]);
}
return (left_max+right_min)/2.0;
}
}
return -1;
}