1、概念
以下摘自百度百科:对于有限的数集,可以通过把所有观察值高低排序后找出正中间的一个作为中位数。如果观察值有偶数个,通常取最中间的两个数值的平均数作为中位数。
由概念可得,对于一个有小到大排序好的数据集,若其长度是n,n为奇数,则中位数就是n/2下标的位置。如果数据集的长度是n,n为偶数,则中位数就是n/2与n/2+1下标的位置的平均值。
获取中位数的主要时间复杂度,由排序算法的时间复杂度决定,最快为nLog(n)。
2、优化解法
首先将数据集的个数分成奇数和偶数两种情况进行探讨,同时发现这两种情况有共同点,如下图所示。数据集数据长度为2n+1或2n时,我们只需要把最大n+1构建成一个新的Collection,再根据奇偶情况计算中位数即可。
而JDK恰巧有这样的数据结构:PriorityQueue(优先队列),其通过完全二叉树实现的小顶堆,插入数据的时间复杂度为Log(n),并且堆顶是最小元素。
Java实现如下:
public class Math {
/**
* 获取中位数
* @param arr
* @return
*/
public Double getMedianNum(int[] arr){
//边界值
if(arr == null || arr.length == 0){
return null;
}
//考虑到arr长度为1时交由下面业务逻辑处理耗时也耗空间,此处做提前判断时间复杂度也才多O(1)
if(arr.length == 1){
return (double) arr[0];
}
// 默认是小顶堆,大顶堆为new PriorityQueue<>(Collections.reverseOrder())
PriorityQueue<Integer> minPQ = new PriorityQueue<>();
int length = arr.length;
int k = length/2+1;
for(int i = 0;i<k;i++){
minPQ.add(arr[i]);
}
for(int i = k;i<length;i++){
if(minPQ.peek() < arr[i]){
minPQ.poll();
minPQ.add(arr[i]);
}
}
if (length % 2 == 0){
return (minPQ.poll() + minPQ.peek())/ 2.0;
} else{
return minPQ.peek().doubleValue();
}
}
}