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)).
1. 第一次看到此题时,我的想法是现在A中折半查找缩小范围,比如取得A的中间数A[i],然后在B中找到A[i],然后便可以缩小范围。A中没有再找B。这个与最后的推荐做法有些类似。
2. 错误或当时没想清楚的是,在B中找A[i]需要log(n)的时间,但其实不需要找A[i],直接找B[(m+n)/2 -i]作比较就行,是O[1]的时间,这样,整体就是O(log(n+m))。 此方法是在此处有描述:http://www2.myoops.org/course_material/mit/NR/rdonlyres/Electrical-Engineering-and-Computer-Science/6-046JFall-2005/30C68118-E436-4FE3-8C79-6BAFBB07D935/0/ps9sol.pdf
3. 另外的方法是:http://leetcode.com/2011/03/median-of-two-sorted-arrays.html 这篇文章提到的方法自己也承认,太复杂太难写了。其思想是先找A的中位数和B的中位数比大小,然后可得到两个数组都可舍弃一半,但舍弃的时候两个数组要舍弃同样数目的数字,这样才能继续实施算法时,新数组们的中位数是原两数组的中位数。
4. 一开始以为要对第一个和第二个中位数分别查找(n+m为偶数时),后来发现只要找到第一个中位数,第二个要么出现在同数组右边,要么在对应数组固定位置。
5. 使用推荐的做法编程时,发现是个很难写对的程序,需要考虑很多情况,同时经常有小错误。
6. 发现个小办法有点用:从最小的数组开始,然后慢慢变大。比如{1} {2,3} => {1,4} {2,3},这样能用较少的次数在纸上覆盖较多的情况。
7. 先多列出一些test case。无论面试还是写程序,都会有好处。
class Result { public int[] array; public int index; } public class Solution { public double findMedianSortedArrays(int A[], int B[]) { // Start typing your Java solution below // DO NOT write main() function // special cases if (A.length == 0){ if (B.length % 2 == 0) { return (B[B.length / 2 - 1] + B[B.length / 2]) / 2.0; } else { return B[B.length / 2]; } } else if (B.length == 0) { if (A.length % 2 == 0) { return (A[A.length / 2 - 1] + A[A.length / 2]) / 2.0; } else { return A[A.length / 2]; } } else if (A.length == 1 && B.length == 1) { return (A[0] + B[0]) / 2.0; } Result r = findMedFromFirstArray(A, B); if (r.index == Integer.MIN_VALUE){ r = findMedFromFirstArray(B, A); } if (( A.length + B.length) % 2 == 0) // need to find med2 { int med2 = findMed2(r, A, B); return (r.array[r.index] + med2) / 2.0; } else { return r.array[r.index]; } } private Result findMedFromFirstArray(int A[], int B[]) { int la = 0; int ra = A.length - 1; int total = 0; if (( A.length + B.length) % 2 == 0) { total = (A.length + B.length)/2 - 2; } else { total = (A.length + B.length)/2 - 1; } while (la <= ra) { int mid = (la + ra) / 2; int idx = total - mid; if ((idx < 0 && (idx + 1) >= 0 && A[mid] <= B[idx + 1]) || (idx >= 0 && idx < B.length && idx + 1 >= B.length && B[idx] <= A[mid]) || (idx >= 0 && idx + 1 < B.length && B[idx] <= A[mid] && A[mid] <= B[idx + 1])) { Result r = new Result(); r.index = mid; r.array = A; return r; } if (idx >= B.length) { la = mid+1; } else if (idx < 0) { ra = mid-1; } else if (B[idx] > A[mid]) { la = mid + 1; } else { ra = mid - 1; } } Result r = new Result(); r.index = Integer.MIN_VALUE; // this means not find r.array = A; return r; } private int findMed2(Result r, int ArrayA[], int ArrayB[]) { int target1 = Integer.MAX_VALUE; int target2 = Integer.MAX_VALUE; int[] A = ArrayA; int[] B = ArrayB; if (r.array == B) { A = ArrayB; B = ArrayA; } { if (r.index + 1 < A.length) { target1 = A[r.index + 1]; } int idx = ( A.length + B.length) / 2 - 2 - r.index + 1; if (idx >= 0 && idx < B.length) { target2 = B[idx]; } if (target1 < target2) { return target1; } else { return target2; } } } }
Discuss上有这段代码,理解一下,基本上是在A中折半猜是不是中位数,A中没有后在B中猜。最后猜到B[j]<=A[i]<=B[j+1],此时,无论奇偶(2k+1或者2k个),A[i]都是第k+1那个。那么奇数时,A[i]是正中的那个;偶数时,A[i]是中位数两个里大的那个,小的那个要从B[j]和A[i-1]里选一个。
double findMedian(int A[], int B[], int l, int r, int nA, int nB) { if (l > r) return findMedian(B, A, max(0, (nA+nB)/2-nA), min(nB, (nA+nB)/2), nB, nA); int i = (l+r)/2; int j = (nA+nB)/2 – i – 1; if (j ≥ 0 && A[i] < B[j]) return findMedian(A, B, i+1, r, nA, nB); else if (j < nB-1 && A[i] > B[j+1]) return findMedian(A, B, l, i-1, nA, nB); else { if ( (nA+nB)%2 == 1 ) return A[i]; else if (i > 0) return (A[i]+max(B[j], A[i-1]))/2.0; else return (A[i]+B[j])/2.0; } }