题目来源:https://leetcode-cn.com/problems/find-median-from-data-stream/
大致题意:
新建一个MedianFinder类,实现其构造函数,并且可以实现 void addNum(int num) 方法和 double findMedian() 方法。
思路
二分插入排序
- 使用一个List列表来存储添加的元素
- 每次添加元素时二分查找插入的位置,保证插入后数组仍然有序。
- 返回中位数时判断列表长度,长度奇偶性不同返回的结果也不同
代码:
public class MedianFinder {
private List<Integer> list;
public MedianFinder() {
list = new ArrayList<Integer>();
}
public void addNum(int num) {
// 二分查找插入位置
int index = binarySearch(num);
list.add(index, num);
}
public int binarySearch(int num) {
int left = 0;
int right = list.size();
while (left < right) {
int mid = (left + right) / 2;
if (list.get(mid) < num) {
left = mid + 1;
}
else {
right = mid;
}
}
return left;
}
public double findMedian() {
int length = list.size();
// 因奇偶性不同返回不同的值
if (length % 2 == 1) {
return list.get(length / 2) * 1.0;
}
else {
return (list.get(length/2 - 1) + list.get(length/2)) * 1.0 / 2;
}
}
}
优先队列
使用两个优先队列维护大于中位数的数和小于中位数的数。
- queMin:最大堆,堆顶元素即为当前堆中最大的元素。用其维护小于中位数的数
- queMax: 最小堆,堆顶元素即为当前堆中最小的元素,用其维护大于中位数的数
每次插入时判断:
- 若当前数小于等于 queMin 堆顶元素,那么插入 queMin 堆。插入后判断 queMin 长度 - 1 是否大于 queMax 的长度,若大于则 queMin 堆顶元素弹出,并插入 queMax
- 若当前数大于 queMin 堆顶元素,那么插入 queMax 堆。插入后判断 queMax 长度 是否大于 queMin 的长度,若大于则 queMax 堆顶元素弹出,并插入 queMin
找中位数时判断 queMin 长度是否大于 queMax 长度,若大于直接返回 queMin 堆顶元素;否则,返回两个堆堆顶元素的平均值。
代码:
public class MedianFinderPro {
public PriorityQueue<Integer> queMin;
public PriorityQueue<Integer> queMax;
public MedianFinderPro() {
// 降序,大的数在队列头
queMin = new PriorityQueue<Integer>((a, b) -> (b - a));
// 升序,小的数在队列头
queMax = new PriorityQueue<Integer>((a, b) -> (a - b));
}
public void addNum(int num) {
// 若插入第一个元素,或者当前数小于等于queMin队头
if (queMin.isEmpty() || num <= queMin.peek()) {
queMin.offer(num);
// 均分两个队列元素
if (queMax.size() < queMin.size() - 1) {
queMax.offer(queMin.poll());
}
}
// 当前数大于queMin队头
else {
queMax.offer(num);
// 均分两个队列元素
if (queMin.size() < queMax.size()) {
queMin.offer(queMax.poll());
}
}
}
public double findMedian() {
// queMin长度大于queMax,中位数在queMin队头
if (queMin.size() > queMax.size()) {
return queMin.peek()*1.0;
}
// 返回两个队头的平均值
else {
return (queMax.peek() + queMin.peek())*1.0 / 2;
}
}
}