题目要求时间复杂度为log(m+n)。
可以将此题转换成寻找第k小的数的问题!
算法的原理:
假设要找第k小的数,我们要比较A[k/2-1]和B[k-k/2-1]这两个元素,这两个元素分别表示A的第k/2小的元素和B的第k-k/2小的元素。至于为什么要去这两个数,是因为k/2+(k-k/2)等于k,所以去这两个数比较。
这两个元素比较共有三种情况:>、<和=:
如果A[k/2-1]<B[k/2-1],这表示A[0]到A[k/2-1]的元素都在A和B合并之后的前k小的元素中,直接抛弃即可。
如果A[k/2-1]>B[k/2-1]时存在类似的结论。
如果A[k/2-1]=B[k/2-1]时,我们已经找到了第k小的数,也即这个相等的元素
通过上面的分析,我们即可以采用递归的方式实现寻找第k小的数。此外我们还需要考虑几个边界条件:
如果A或者B为空,则直接返回B[k-1]或者A[k-1];
如果k为1,我们只需要返回A[0]和B[0]中的较小值;
完整代码:
public static double findMedianSortedArrays(int A[], int m, int B[], int n) {
int total = m + n;
if ((total & 0x1) != 0)
return findKth(A, m, B, n, total / 2 + 1, 0, 0);
else
return (findKth(A, m, B, n, total / 2, 0, 0) + findKth(A, m, B, n,total / 2 + 1, 0, 0)) / 2;
}
public static double findKth(int a[], int m, int b[], int n, int k, int head_a, int head_b) {
if (m > n)
return findKth(b, n, a, m, k, head_b, head_a);
if (m == 0)
return b[k - 1+head_b];
if (k == 1)
return Math.min(a[head_a], b[head_b]);
int pa = Math.min(k / 2, m), pb = k - pa;
int new_pa = head_a+(pa-1),new_pb = head_b+(pb-1);
if (a[new_pa] < b[new_pb])
return findKth(a, m - pa, b, n, k - pa, head_a += pa, head_b);
else if (a[new_pa] > b[new_pb])
return findKth(a, m, b, n - pb, k - pb, head_a, head_b += pb);
else
return a[new_pa];
}
代码解释:
①head_a和head_b:表示抛弃无用元素后,数组下一次开始的位置
②pa和pb:计算理论上要比较的两个元素的位置
③new_pa和new_pb:因为抛弃元素后,数组的起始位置往后移了,所以需要重新计算要比较的两个元素的实际所在位置
④在递归调用findKth()方法时,参数的解释:m-pa表示抛弃元素后数组a剩余元素的长度;k-pa表示因为抛弃了pa个元素,所以不能再寻找第k小的数了要变成寻找第k-pa小的数;抛弃pa个元素后,数组a的起始是从head_a += pa开始
在最好情况下,每次都有k一半的元素被删除,所以算法复杂度为logk,由于求中位数时k为(m+n)/2,所以算法复杂度为log(m+n)。
连接:
http://blog.csdn.net/yutianzuijin/article/details/11499917
http://blog.csdn.net/fightforyourdream/article/details/17351395