题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解
维护一个大顶堆和一个小顶堆。大顶堆存储较小的一半元素,小顶堆存储较大的一半元素。
每次添加元素时,根据两个顶堆的元素数量进行处理:
设小顶堆为h1,长度为m;大顶堆为h2,长度为n
-
m == n
插入大顶堆h2, 大顶堆h2的堆顶元素添加到小顶堆h1;//这时候看上去是插入到了大顶堆,实际上是插入到了小顶堆,因此h1 > h2 -
m != n
插入到小顶堆h1,并将小顶堆堆顶元素插入大顶堆h2。 //实际大顶堆增加,但因为小顶堆本来就比较大,因此会插入到小顶堆
获取中位数:
3. m == n
获取两个顶堆元素的平均值;
4. m != n
获取小顶堆的堆顶元素。
注意:这里的大顶堆和小顶堆的位置可以互换,但是必须保证大顶堆和小顶堆的堆顶为中间的数字。而哪边比较多其实无所谓。
可以使用java提供PriorityQueue来实现,内部使用堆排序,每次都是完全排序好的;
也可以自己手写堆化方法,其实也很简单。
PriorityQueue:
Queue<Integer> h1, h2;
public MedianFinder() {
h1 = new PriorityQueue<>();
h2 = new PriorityQueue<>((o1, o2) -> o2 - o1);
}
public void addNum(int num) {
if (h1.size() != h2.size()) {
h2.offer(num);
h1.offer(h2.poll());
} else{
h1.offer(num);
h2.offer(h1.poll());
}
}
public double findMedian() {
return h1.size() == h2.size() ? (h2.peek() + h1.peek()) / 2.0 : h2.peek();
}
}
自定义堆化方法:
class MedianFinder {
/** initialize your data structure here. */
List<Integer> h1, h2;
public MedianFinder() {
h1 = new ArrayList<>();
h2 = new ArrayList<>();
}
public void addNum(int num) {
if (h1.size() == h2.size()) {
h2.add(num);
heapify2(h2.size() - 1,h2);
h1.add(h2.remove(0));
heapify1(h1.size() - 1, h1);
heapify2(h2.size() - 1,h2);
}else{
h1.add(num);
heapify1(h1.size() - 1,h1);
h2.add(h1.remove(0));
heapify1(h1.size() - 1,h1);
heapify2(h2.size() - 1,h2);
}
}
public double findMedian() {
return h1.size() == h2.size() ? (h1.get(0) + h2.get(0)) / 2.0 : h1.get(0);
}
public void heapify1(int len, List<Integer> list) {
for (int i = len / 2; i >= 0; i--) {
int t = i;
int left = i * 2 + 1, right = i * 2 + 2;
if (left <= len) {
if (list.get(i) > list.get(left)) t = left;
}
if (right <= len) {
if (list.get(i) > list.get(right)) t = right;
}
if (t != i) {
int temp = list.get(t);
list.set(t, list.get(i));
list.set(i, temp);
}
}
}
public void heapify2(int len, List<Integer> list) {
for (int i = len / 2; i >= 0; i--) {
int t = i;
int left = i * 2 + 1, right = i * 2 + 2;
if (left <= len) {
if (list.get(i) < list.get(left)) t = left;
}
if (right <= len) {
if (list.get(i) < list.get(right)) t = right;
}
if (t != i) {
int temp = list.get(t);
list.set(t, list.get(i));
list.set(i, temp);
}
}
}
}