您的位置:首页 > 其它

寻找两个数组的中位数 Median of Two Sorted Arrays ***问题转化思想***

2013-11-20 14:46 429 查看

题目源自于Leetcode。经典好题。

题目:

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)).给定两个有序数组,长度不一定相同,一个是m、一个是n,要求给出他们合并在一起之后的数组的中位数。要求时间复杂度为O(log(m+n)),所以不可以合并数组再找中位数,否则复杂度就是O(m+n)。

数组的中位数

如果有序数组有奇数个元素,那么中位数是最中间那个元素。如果有序数组有偶数个元素,那么中位数是中间两个元素的平均数。

思路:

    这道题目看起来容易,本以为二分递归就行了。但是写了好久都写不出来。因为情况比较复杂,当两个数组元素个数都为奇数或偶数,或一奇一偶时,当各自中位数相同或不同时,情况都不一样。中位数如果是两个数字的平均数,这两个数字可能来自于同一个数组,也可能来自两个数组。总之是很混乱。写了大概有好几个小时,始终不能搞定。觉得这个思路看来不对。只能google了。    看到别人对这道题的思路,不是二分查找直接找中位数,而是将核心思想转化为去找两个数组的从小到大排的第k个数。两个数组共有m+n个数,若m+n是奇数,那么就是找第(m+n)/2+1个数,偶数的话就是找第(m+n)/2个数和第(m+n)/2+1个数的平均数。转换成找第k个元素之后,在二分查找时就避开了奇数偶数情况不同的问题,只管去第k个元素。

    具体的做法是这样的:

    假设我现在要在数组A和数组B中查找第k个数(假设A的长度m不大于B的长度n,这里k一定不大于m+n)。    即,当前问题:找A和B的第k个数。(很显然,数列“前x个数的最右数”就是数列的“第x个数”。所以找第x个数和找前x个数是一样的。因为一旦确定了前x个数,这些数的最右数就是第x个数。)    我先假定最终的前k个数中有一半(k/2)在A中,另一半(k-k/2)在B中。当然,如果A中总个数m少于k/2,我只好假定最终的前k个数中有m个在A中,另外k-m个在B中。    做好上面的假定之后,根据我的假定,A中的在前k个数中的数的最右数是第pa个数,B中的在前k个数中的数的最右数叫第pb个数。1、如果第pa数等于第pb数,那么最终的前k个数的最右数一定等于第pa数(是pa还是pb无所谓了),也就是说最终的第k个数就是第pa数。2、如果第pa数大于第pb数,那么说明,我所假定的B中目前属于前k个数的数确实是属于最终的前k个数,并且后面还有部分数也属于。我所假定的A中目前属于前k个数的数不一定都属于最终的前k个数。好了,于是我就把B中确实是属于最终的前k个数的这pb个数(即第pb和第pb之前的这些数)切掉,变成B'。问题就变为去找A和B'中的前k-pb个数。3、如果第pa数小于第pb数,那么说明,我所假定的A中目前属于前k个数的数确实是属于最终的前k个数,并且后面还有部分数也属于(有可能后面已经没数了)。我所假定的B中目前属于前k个数的数不一定都属于最终的前k个数。好了,于是我就把A中确实是属于最终的前k个数的这pa个数(即第pa和第pa之前的这些数,有可能就是A中所有的数)切掉,变成了A'(可能是空的)。问题变为去找A'和B中的前k-pa个数。根据上面的算法,可以看出是在很努力的折半,但是每次折半也只是折半其中某一个数组。之前我的错误想法一直是在同时折半两个数组,所以得不到正确结果。public class MedianOfTwoSortedArrays {
public double findMedianSortedArrays(int A[], int B[], int m, int n) {
// Start typing your Java solution below
// DO NOT write main() function

int lowA =0;
int lowB = 0;
int highA = m - 1;
int highB = n - 1;
int k = (m + n) / 2;
if((m + n) % 2 == 0){
return (findKth(A, B, lowA, highA, lowB, highB, k) + findKth(A, B, lowA, highA, lowB, highB, k + 1)) / 2.0;
}
else
return findKth(A, B, lowA, highA, lowB, highB, k);
}

public int findKth(int A[], int B[], int lowA, int highA, int lowB, int highB, int k){
int m = highA - lowA + 1;
int n = highB - lowB + 1;
if (m == 0)
return B[lowB + k];
if (n == 0)
return A[lowA + k];
if (k == 0 )
return A[lowA] > B[lowB] ? B[lowB] : A[lowA];

/*
int midA = (lowA + highA) / 2;
int midB = (lowB + highB) / 2;
if(A[midA] > B[midB]){
if((midA + midB + 1) >= k)
return findKth(A, B, lowA, midA, lowB, highB, k);
else
return findKth(A, B, lowA, highA, midB + 1, highB, k - (n / 2 + 1));
}
else{
if((midA + midB + 1) >= k)
return findKth(A, B, lowA, highA, lowB, midB, k);
else
return findKth(A, B, midA + 1, highA, lowB, highB, k - (m / 2 + 1));
}
*/
// Reduce search ranges in A and B
int midA = m * k / (m + n);
int midB = k - midA - 1;
// Add offset so that imid_A and imid_B index directly into A and B, respectively
midA += lowA;
midB += lowB;
if (A[midA] > B[midB]) {
k -= midB - lowB + 1;
highA = midA;
lowB = midB + 1;
}
else {
k -= midA - lowA + 1;
lowA = midA + 1;
highB = midB;
}
return findKth(A, B, lowA, highA, lowB, highB, k);
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: