Median of Two Sorted Arrays
There are two sorted arrays a and a 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)).
eg.
a=[1,2]
b=[3,4]
median=2.5
解法二运行结果:60ms, beats 95.99%
解法一:归并想法(Merge)
(不符合时间要求)
依次找到min(a[i],b[j]) 直到第N/2个即为中位数
N= m+n
时间复杂度O(N/2)) 空间复杂度O(1)
public double merge(int[] a, int[] b) {
int N = a.length + b.length;
int count = 0;
int mid1 = 0;
int mid2 = 0;
int i = 0, j = 0;
for (; count < (N + 1) / 2; count++) {
if (a[i] < b[j]) {
mid1 = a[i];
i++;
} else {
mid1 = b[j];
j++;
}
// System.out.print(mid1 + " ");
}
if (N % 2 == 0) {
if (a[i] < b[j]) mid2 = a[i];
else mid2 = b[j];
// System.out.println(mid2);
} else {
mid2 = mid1;
// System.out.println();
}
return (mid1 + mid2) / 2.0;
}
引:中位数
中位数和奇偶有关。
N为奇数,中位数为第(N+1)/2个数==第(N+2)/2个数
N为偶数,中位数为第(N)/2个数==第(N+1)/2个数 和 第(N+2)/2个数 的平均值
可以看出无论奇偶时,有神奇的对称美的方法。
然并软,当N为奇数时,中位数是相同的,实际这样做在递归时会算两遍,增加运行时间,因此看看感叹一下就好。
(前提是Java对整数的自动向下取整)
median=0.5*(a[(N-1)/2]+a[(N)/2]);
注意点1:奇偶性
注意点2:第k个数,在数组的下标为k-1
扯句题外话,初中时学的时候奇偶的题想着枚举一下就好了,看这道题的时候内心有点崩溃。
这道题解法二的二分法的主要的难点也就在理清各种奇偶性和边界条件上了。
解法二:二分法(递归)
时间复杂度O(log(m+n))
解题思路:
将a数组的中间元素与b数组的“中间元素”相比较,从而删掉较小元素所在数组的前一半和较大元素所在数组的后一半。递归下去。
思考歧路,反思与总结:
1.题目改为寻找第k小的元素findKthSortedArrays。
(这里k从1开始计算,k和下标的区别很容易混淆)
这个思路的转变很关键。
原因在于,如果我们执着于median,那么在删除一半左右元素形成的子问题中,很难保证仍然是寻找median。可能变为寻找median前一个或者后一个。(因为删除的过程中,两个数组是分开的,那么奇偶的属性会被遗漏,到最后无法知道到底要一个还是两个)
改成第k小元素的好处就是,我们可以将删掉的元素考虑进来,在子问题中不断改变k的值。
(例如:本来我们需要寻找的median是第5个数,删掉前面2个数之后,在子问题中就变为寻找第5-2=3个数)
考虑a、b数组总数的奇偶性问题,就转化为调用findKthSortedArrays的问题了。
2.实现findKthSortedArrays
1)第k小的k值设定
a.当k==1时,第k小即最小
只需比较两个数组的第一项 a[0]和b[0]
实际情况因为是递归 所以为给定参数的起始值 即比较a[alo]和b[blo] 返回最小值
b.当k>=2时,第k小转化为在每个数列中取前k一半或一半不到的数,具体为k/2个数,取到a[k/2-1]和b[k/2-1]
Q: 为什么取到a[k/2-1]和b[k/2-1],而不是k/2? (k-1)/2?
如果k==2 就是需要比较头两个元素,因此比较的数下标为0,删去1个。
(删去的为较小的那部分,包括被比较的自身,其为前k/2小,肯定不可能为第k个元素。说是删除,其实只是移动数组的指针。)
(为什么要删去自身?可以不删去自身吗?删去的话可以明确这个数已经被比较过了,若不删去,不知道它到底是待比较留下的还是还未比较的)
如果k==3,就是需要比较头两个元素,因此比较的数下标为0,删去1个;如果比较头四个元素,比较的数下标为1,删去2个,会把第3个要求的给删掉了。
综上归纳而得。
2)返回值设置
1)当k==1时,返回a[0]、b[0]中小的那个
2)a数组为空时,返回b数组第k个元素
3)b数组为空时,返回a数组第k个元素
3)递归
若a[k/2-1]<b[k/2-1],则从a的中间值index的后一个,即a[index_lo+(k/2-1)+1]开始,和数组b一起找第k-k/2个数
做法一:
int i = k / 2 - 1;
int aMid = Integer.MAX_VALUE, bMid = Integer.MAX_VALUE;
if (alo + i < a.length) aMid = a[alo + i]; //小于a.length 而不是 a.length-1
if (blo + i < b.length) bMid = b[blo + i];
if (aMid < bMid)
return find(a, alo + i + 1, b, blo, k - i - 1);
else return find(a, alo, b, blo + i + 1, k - i - 1);
做法二(自己做的,超过时间):
int i = k / 2 - 1;
if (alo + i > a.length - 1) i = a.length - 1 - alo;
if (blo + i > b.length - 1) i = b.length - 1 - blo;
if (a[alo + i] < b[blo + i])
return find(a, alo + i + 1, b, blo, k - i - 1);
else return find(a, alo, b, blo + i + 1, k - i - 1);
4)边界问题:寻找第k小,若数组长度<k怎么办?
如第8小,a的长度为2,b的长度为20
k=8;i=3;
做法一:
将b[3]及之前共4个数删去后,a,b继续找第8-4=4小
k'=4;i=1;
比较a[1]和b[1]...
做法二:
i=2-1=1;
比较a的最后一个,a[1]和b[1]
若a<b,继续找第7小,此时a已经删除光,递归后即刻返回;
反之,继续找第7小,之后可能继续找第6小,第5小...可见运气最坏搜寻时间会变成线性。
边界问题vs返回问题
边界:超出边界,a中仍有元素,不能直接跳过
返回:a为空,可直接跳过只看b
代码:
public class Q4_FindMedianSortedArrays {
public double findMedianSortedArrays(int[] a, int[] b) {
int N = a.length + b.length;
int m1 = find(a, 0, b, 0, (N + 1) / 2);
int m2 = m1;
if (N % 2 == 0) m2 = find(a, 0, b, 0, (N + 2) / 2);
return (m1 + m2) / 2.0;
}
public int find(int[] a, int alo, int[] b, int blo, int k) {
if (alo > a.length - 1) return b[blo + k - 1];
if (blo > b.length - 1) return a[alo + k - 1];
if (k == 1) return Math.min(a[alo], b[blo]);
int i = k / 2 - 1;
int aMid = Integer.MAX_VALUE, bMid = Integer.MAX_VALUE;
if (alo + i < a.length) aMid = a[alo + i];
if (blo + i < b.length) bMid = b[blo + i];
if (aMid < bMid) return find(a, alo + i + 1, b, blo, k - i - 1);
else return find(a, alo, b, blo + i + 1, k - i - 1);
}
}
待补充
顺序统计问题(Order Statistics)
参考链接:
http://blog.csdn.net/ai552368...
http://www.cnblogs.com/gangan...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。