优化添加数字以排队以获取数字流的中位数

时间:2019-05-24 07:14:10

标签: java priority-queue median

最近在一次采访中有人问我这个问题,以便从数字数据流中找到中位数,然后我提出了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方法的事情吗?

1 个答案:

答案 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);
    }
}