Median of Two Sorted Arrays
There are two sorted arrays A and B 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)).
这个题目是非常有名的面试题,
注意leetcode上面对中位数的定义是:
如果合并之后的数组元素个数是奇数,那么中位数就是中间的那位
如果合并之后的数组元素个数是奇数,那么中位数是中间两位的平均数
解题思路有三种
解法一:
使用合并排序,将两个数组重新排序成一个新的数组,这样在O(1)的时间可以找到中位数,排序的时间是O(n+m)
解法二:
先假设数组A的长度为m,数组B的长度为n
1. 先分别找出数组A和数组B的中位数 ma 和 mb,设A[i] = ma , B[j] = mb 然后比较大小
2. 如果 ma < mb, 即A[i] < B[j], 那么中位数一定在A[i..m] 和 B[1..j] 之间, 然后再这个区间再求中位数,递归查找
3. 如果 ma > mb, 即A[i] > B[j],那么中位数一定在A[1..i] 和 B[j..n] 之间,然后再这个区间再求中位数,递归查找
算法比较好理解,但是程序有两点需要注意:
1) 递归的base条件是,当其中一个数组元素个数只剩下小于等于2时,递归结束
2) 每次递归时,在两个数组中选择除去的元素一定要相等。比如说A数组减去两个元素,那么B数组也要减去两个,这样剩下的区间的中位数和原来的中位数是一样的
程序如下: 这个代码过了leetcode的所有case,算是bug free了。
/**
* find median of two sorted array, the values in array may same
*
* <p>
* The algorithm are:
* </p>
* <ol>
* <li>Calculate the medians m1 and m2 of the input arrays A and B
* respectively.</li>
* <li>If m1 is less than m2, then the median located between
* A[m-index(m1)..m] and B[0..index(m2)], index(m1),index(m2) are the index
* of array A and B,repeat finding median recrusively</li>
* <li>If m1 is greater than m2, then the median located between
* A[0..index(m1)] and B[n-index(m2)..n] ,repeat finding median recrusively</li>
* </ol>
*
*/
static class FindMedian1 {
public static double medianSelect(int[] A, int[] B) {
if (A == null && B == null) {
return -1;
} else if ((A == null || A.length == 0) && B.length != 0) {
return B.length % 2 == 1 ? B[(B.length - 1) / 2]
: (B[(B.length - 1) / 2] + B[(B.length - 1) / 2 + 1]) / 2.0;
} else if (B == null || B.length == 0 && A.length != 0) {
return A.length % 2 == 1 ? A[(A.length - 1) / 2]
: (A[(A.length - 1) / 2] + A[(A.length - 1) / 2 + 1]) / 2.0;
}
return findMedian(A, 0, A.length - 1, B, 0, B.length - 1);
}
public static double findMedian(int[] A, int p, int r, int[] B, int l,
int h) {
if (p == r) {// only one element left to compare
return findMedianBaseCase1(A[p], B, l, h);
} else if (l == h) {
return findMedianBaseCase1(B[l], A, p, r);
} else if (r == p + 1) {
return findMedianBaseCase2(A[p], A[r], B, l, h);
} else if (h == l + 1) {
return findMedianBaseCase2(B[l], B[h], A, p, r);
}
int medA = (p + r) / 2;
int medB = (l + h) / 2;
if (A[medA] <= B[medB]) {
int k = 0;
if ((r - p + 1) % 2 == 0) {
k = Math.min(medA - 1, h - medB - 1);
} else {
k = Math.min(medA, h - medB - 1);
}
k = k == 0 ? 1 : k;
return findMedian(A, p + k, r, B, l, h - k);
} else {
int k = 0;
if ((h - l + 1) % 2 == 0) {
k = Math.min(medB - 1, r - medA - 1);
} else {
k = Math.min(medB, r - medA - 1);
}
k = k == 0 ? 1 : k;
return findMedian(A, p, r - k, B, l + k, h);
}
}
public static double findMedianBaseCase1(int med, int[] A, int p, int r) {
if (r == p) {
return (med + A[p]) / 2.0;
}
if (med == A[(r + p) / 2]) {
return med;
} else if (med < A[(r + p) / 2]) {
if ((r - p + 1) % 2 == 0) {
return A[(r + p) / 2];
} else {
return (Math.max(med, A[(r + p) / 2 - 1]) + A[(r + p) / 2]) / 2.0;
}
} else {// if(med > A[(r+p)/2])
if ((r - p + 1) % 2 == 1) {
return (A[(r + p) / 2] + Math.min(med, A[(r + p) / 2 + 1])) / 2.0;
} else {
return med < A[(r + p) / 2 + 1] ? med : A[(r + p) / 2 + 1];
}
}
}
// med2 > med1 by default
public static double findMedianBaseCase2(int med1, int med2, int[] A,
int p, int r) {
int medA = (r + p) / 2;
if (r == p + 1) {
if (med1 > A[p]) {
if (med2 < A[r]) {
return (med1 + med2) / 2.0;
} else {
return (med1 + A[r]) / 2.0;
}
} else if (med2 > A[r]) {
return (A[r] + A[p]) / 2.0;
} else {
return (med2 + A[p]) / 2.0;
}
}
if ((r - p + 1) % 2 == 0) {
if (med2 < A[medA]) {
return (Math.max(med2, A[medA - 1]) + A[medA]) / 2.0;
} else if (med1 > A[medA]) {
if (med2 < A[medA + 1]) {
return (med1 + med2) / 2.0;
} else {// med2 > A[medA+1]
return (Math.min(med1, A[medA + 2]) + A[medA + 1]) / 2.0;
}
} else {
return (A[medA] + Math.min(med2, A[medA + 1])) / 2.0;
}
} else {
if (med1 > A[medA]) {
return Math.min(A[medA + 1], med1);
} else if (med2 < A[medA]) {
return Math.max(med2, A[medA - 1]);
} else {
return A[medA];
}
}
}
}