您的位置:首页 > 其它

Median of Two Sorted Arrays

2015-01-21 20:36 337 查看
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)).

下面是O(nlgn)的解法

public class Solution {
public double findMedianSortedArrays(int A[], int B[]) {
int[] C = new int[A.length + B.length];
for(int i = 0; i < A.length; i++){
C[i] = A[i];
}
for(int i = 0; i < B.length; i++){
C[A.length + i] = B[i];
}
Arrays.sort(C);
if((A.length + B.length) % 2 == 1){
return C[(A.length + B.length) / 2];
}else{
return (C[(A.length + B.length) / 2] + C[(A.length + B.length) / 2 - 1]) / 2d;
}
}
}


还有O(m+n)的解法,两个对象指向数组开头,往后移动,直到找到第m+n/2大的。

public class Solution {
public double findMedianSortedArrays(int A[], int B[]) {
int i =0;
int j = 0;
int m = A.length;
int n = B.length;
double median = 0d;
double last = 0d;
int count = 0;

while(count <= (m + n) / 2){
last = median;
//如果A中的元素已经用完,直接取B数组
if(i == m){
median = B[j];
j++;
}else if(j == n){//如果B中的元素已经用完,直接取A数组
median = A[i];
i++;
}else if(A[i] < B[j]){
median = A[i];
i++;
}else if(A[i] >= B[j]){
median = B[j];
j++;
}
count ++;
}

if((A.length + B.length) % 2 == 1){
return median;
}else{
return (median + last) / 2d;
}
}
}


最后提到的是一个极为巧妙地方法。可以在O(logm+logn)的时间内解决问题。看到时间复杂度很容易想到分治求解,可分治的模型很难确定,这里给了一个比较巧妙的思路。假定m+n为奇数,我们只要求第(m+n)/2 +1个,偶数则是求(m+n)/2 +1与(m+n)/2大数的平均数。我们令(m+n)/2+1=k。就是求A+B中第K大数的问题,只不过中位数正好是一半。

考虑在A和B中各取一半,即K/2的数字进行比较。如果这时候A[k/2- 1] < B[k/2 - 1],证明A[k/2 -1]往左的数字,都在前k小的数字里,真正的临界点在A[k/2 -1]往后,那么就把A前面的舍弃,从A[k/2]开始,包括B的所有数字,寻找剩下的第k/2个。同样,如果B[k/2 - 1]小,证明这个第k大的还在B[k/2 - 1]之后,于是舍弃B[k/2 - 1]往前的数字,在剩下的中继续寻找。如此形成一个分治的解决方法。

这里要注意,如果k/2>A的元素数量,就代表A中的元素全部在第K大数字之前,全部舍去。实际上就是直接在B中找第k-m-1个了。



public class Solution {
public static double findKthElement(int A[], int B[], int k){
int m = A.length;
int n = B.length;
//always assume that m is equal or smaller than n
if(m > n){
return findKthElement(B, A, k);
}

if(m == 0 & n == 0){
return 0;
}
if(m == 0){
return B[k - 1];
}
if(n == 0){
return A[k - 1];
}

if (k == 1)
return Math.min(A[0], B[0]);

int pa = Math.min(m, k / 2);
int pb = k - pa;
//递归直至该条件结束
if(A[pa - 1] == B[pb - 1]){
return A[pa - 1];
}else if(A[pa - 1] < B[pb - 1]){//舍去A[pa-1]之前的元素,重新搜索第k-pa个元素
int[] C = new int[m - pa];
for(int i = 0; i < m - pa; i++){
C[i] = A[pa + i];
}
return findKthElement(C, B, k - pa);
}else{//舍去B[pb-1]之前的元素,重新搜索第k-pb个元素
int[] C = new int[n - pb];
for(int i = 0; i < n - pb; i++){
C[i] = B[pb + i];
}
return findKthElement(A, C, k - pb);
}
}
public double findMedianSortedArrays(int A[], int B[]) {
int m = A.length;
int n = B.length;

if((m + n) % 2 == 1){
return findKthElement(A, B, (m + n) / 2 + 1);
}else{
return (findKthElement(A, B, (m + n) / 2) + findKthElement(A, B, (m + n) / 2 + 1)) / 2;
}
}
}


update:2015/03/14

再次做这题,还是被搞得晕死。我只能说第k大和数组下标从0开始简直是要搞死人。

K个元素的下标就是k-1。数组元素的数量是end-start+1类似这种,还有k/2-1。稍有不注意就会做错。

public class Solution {
public double findMedianSortedArrays(int A[], int B[]) {
if((A.length + B.length) % 2 == 1){
return findKthSrotedArrays(A, 0, A.length - 1, B, 0, B.length - 1, (A.length + B.length) / 2 + 1);
}else{
return (findKthSrotedArrays(A, 0, A.length - 1, B, 0, B.length - 1, (A.length + B.length) / 2) +
findKthSrotedArrays(A, 0, A.length - 1, B, 0, B.length - 1, (A.length + B.length) / 2 + 1)) / 2d;
}
}

//第k大的下标在k-1,第k/2的坐标在k/2-1
public int findKthSrotedArrays(int[] A, int startA, int endA, int[] B, int startB, int endB, int k){
//假设A长度小于B,否则就反过来搜,这样只要考虑A和k/2-1的大小就可以了
if(endA- startA > endB - startB){
return findKthSrotedArrays(B, startB, endB, A, startA, endA, k);
}
if(startA > endA){
return B[startB + k - 1];
}

if(startB > endB){
return A[startA + k - 1];
}

if (k == 1){
return Math.min(A[startA], B[startB]);
}

int pa = Math.min(startA + k / 2 - 1, endA);    //A中元素有k/2个,坐标为k/2-1 + startA
int pb = startB + k - (pa - startA + 1) - 1;

if(A[pa] == B[pb]){
return A[pa];
}else if(A[pa] < B[pb]){
return findKthSrotedArrays(A, pa + 1, endA, B, startB, endB, k - (pa - startA)- 1);
}else{ // if(A[pa - 1] > B[pb - 1])
return findKthSrotedArrays(A, startA, endA, B, pb + 1, endB, k - (pb - startB) - 1);
}
}
}


最后其实用下面的代码可以同时砍去两段,因为另一段已经肯定不在前k个元素里了。

if(A[pa] == B[pb]){
return A[pa];
}else if(A[pa] < B[pb]){
return findKthSrotedArrays(A, pa + 1, endA, B, startB, pb, k - (pa - startA)- 1);
}else{ // if(A[pa - 1] > B[pb - 1])
return findKthSrotedArrays(A, startA, pa, B, pb + 1, endB, k - (pb - startB) - 1);
}


最后总结一下找第k大元素的方法。二分查找的划分条件:比较A[k/2 - 1]和B[k/2 - 1]的大小(记得上面说的,第k大和数组下标从0开始的痛苦),如果A[k/2 - 1]<B[k/2 - 1],就代表A的窗口要往右扩,B的窗口往左缩,但是它们的含义是不同的。A[k/2 - 1]往左的元素肯定在前k大的元素里,故要舍去,B[k/2-1]往右的元素肯定不在前k/2大的元素里,所以也要舍去。

如果A[k/2 - 1]>B[k/2 - 1],A的窗口往左缩,B的窗口往右扩。也就是舍去B左侧的元素,因为它们都在前k大元素里。

如果A[k/2 - 1]==B[k/2 - 1],返回该值,就是第k大的值。

需要考虑的边界情况:

k/2 - 1很可能大于a.length - 1或者b.length - 1,所以需要判断它们的大小。方便的做法是,永远假设A<B或者A>B。

如果A已经被全部砍去,只要在B中找到剩下第K大的元素即可,反之亦然。

k==1时,不能再看k/2-1了,这时它==-1,所以要拿出来单独讨论。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: