做题前先学习了一个Java优先队列数据结构PriorityQueue
对于优先队列,这篇漫画说的很清楚http://www.sohu.com/a/256022793_478315
Java 的PriorityQueue在构造时需要手写排序方法,具体后文在代码中实现。
在学习完优先队列和堆结构后,看题:
LeetCode 暑期打卡第七周题九:
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
示例:
addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
进阶:
如果数据流中所有整数都在 0 到 100 范围内,你将如何优化你的算法?
如果数据流中 99% 的整数都在 0 到 100 范围内,你将如何优化你的算法?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-median-from-data-stream
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方法一:暴力
暴力法很简单,维护一个保持排序的动态数组,比较无脑,没有意义。(因为我写算法总是先暴力再想着优化,好扯…)
class MedianFinder {
ArrayList<Integer> al;
/** initialize your data structure here. */
public MedianFinder() {
al = new ArrayList<>();
}
public void addNum(int num) {
al.add(num);
Collections.sort(al);
}
public double findMedian() {
int n = al.size();
if((n & 1) == 0){
int index1 = n / 2 - 1;
int index2 = n / 2;
double ans = (al.get(index1) + al.get(index2)) / 2.0;
return ans;
}
else{
int ansIndex = (n - 1) / 2;
return al.get(ansIndex);
}
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
时间复杂度 O (N * log2N) , 主要是排序所耗费的时间。
方法二:对顶堆
主要思想就是将整个序列一分为二,前半段用大顶堆后半段用小顶堆维护,这样做的好处是两个堆都只要取出队元素就是中间两个需要的中位数(或者一个),代码非常直观:
class MedianFinder {
int count = 0;
PriorityQueue<Integer> first;
PriorityQueue<Integer> end;
/**
* initialize your data structure here.
*/
public MedianFinder() {
first = new PriorityQueue<>((x, y) -> {
if (x > y) return -1;
else if (x < y) return 1;
else return x.compareTo(y);
});
end = new PriorityQueue<>((x, y) -> {
if (x > y) return 1;
else if (x < y) return -1;
else return x.compareTo(y);
});
}
public void addNum(int num) {
this.count++;
first.add(num);
end.add(first.poll());
if ((count & 1) == 1) first.add(end.poll());
}
public double findMedian() {
if ((count & 1) == 1) return Double.parseDouble(String.valueOf(first.peek()));
else {
double ans = (first.peek() + end.peek()) / 2.0f;
return ans;
}
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
时间复杂度 O(log2N)