最近在一次采访中有人问我这个问题,以便从数字数据流中找到中位数,然后我提出了Priority Queue
解决方案,如下所示:
public class MedianFinder {
private final PriorityQueue<Long> min = new PriorityQueue<>();
private final PriorityQueue<Long> max = new PriorityQueue<>(Collections.reverseOrder());
public void addNum(long num) {
max.offer(num);
min.offer(max.poll());
if (max.size() < min.size()) {
max.offer(min.poll());
}
}
public double findMedian() {
if (max.size() == min.size())
return (max.peek() + min.peek()) / 2.0;
else
return max.peek();
}
}
现在,面试官要我优化addNum
方法,因为它有很多O(log n)操作(大约5),他想看看我们是否可以进一步对其进行优化,从而减少O(log n)操作?我们在这里可以做些优化addNum
方法的事情吗?
答案 0 :(得分:2)
这可以将offer
呼叫的平均数量从2.5减少到1.5,并将poll
呼叫的平均数量从1.5减少到0.5。总体上将O(log n)操作的平均次数从4个减少到2个。
public void addNum(long num) {
if(!max.isEmpty() )
{
if(max.size() == min.size())
{
if(num > max.peek())
{
min.offer(num);
max.offer(min.poll());
}
else
{
max.offer(num);
}
}
else
{
if(num > max.peek())
{
min.offer(num);
}
else
{
max.offer(num);
min.offer(max.poll());
}
}
}
else
{
max.offer(num);
}
}
更紧凑的版本(逻辑相同)
public void addNum(long num) {
if(!max.isEmpty())
{
(num > max.peek() ? min : max).offer(num);
if(min.size() > max.size())
{
max.offer(min.poll());
}
else if(max.size() - min.size() > 1)
{
min.offer(max.poll());
}
}
else
{
max.offer(num);
}
}