题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
测试用例:
- 功能测试:从数据流中读取偶数或者奇数个数。
- 边界测试:从数据流中读取 0、1、2 个数。
思路:使用最大堆和最小堆两个容器,最大堆用来存放依次传入的数组成的数组中较小的数,最小堆用来存放较大的数。这样的话,若传入的数组总数为偶数,那么中位数为最大堆的堆顶元素和最小堆的堆顶元素和的一半;若传入的数组总数为奇数,那么中位数为最大堆堆顶元素或者是最小堆的堆顶元素,两者为同一个数。
注意必须保证以下两点:
- 两个堆中的数据数目差不能超过1,这样可以使中位数只会出现在两个堆的交接处 ,在Insert()方法中使用了count来辅助实现。
- 小顶堆的元素都小于大顶堆的元素,借用优先队列PriorityQueue。其默认维持队列内升序排列。也可以像上面传入一个比较器,然后使其改变排列顺序。
public class test_forty_one {
int count = 0;
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15, new Comparator<Integer>(){
//优先队列的默认排序为升序排序,传入一个传感器改变默认的排序方式
//目的为使最大堆的最大数位于优先队列的第一位
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
public void Insert(Integer num) {
//利用计数器通过数据总数的奇偶性可以控制进入最大堆和最小堆的数量相差1
//最大堆中存放的是数组左边较小的数,最小堆存放的是数组右边较大的数
if (count%2 == 0){
//每次传入一个数到最大堆,然后把最大堆中最大的数放到最小堆中
maxHeap.offer(num);
int temp = maxHeap.poll();
minHeap.offer(temp);
}
else {
//每次传入一个数到最小堆,然后把最小堆中最小的数放到最大堆中
minHeap.offer(num);
int temp = minHeap.poll();
maxHeap.offer(temp);
}
count++;
}
//注意类型为Double
public Double GetMedian() {
if (count%2 == 0){
return new Double(minHeap.peek()+maxHeap.peek())/2;
}
else {
return new Double( minHeap.peek());
}
}
}