lucene实现的top k优先队列PriorityQueue简单原理

Lucene里使用比较多的一种集合就是这个

PriorityQueue


比如取前10条相关结果。


jdk本身也有一个优先级队列,为什么lucene要实现自己的呢。。

后面看了jdk的 PriorityQueue ,它是使用最大堆来实现的,而且它的长度是什么可以变长的,就是如果我要一个top k的数据,但它会将所有数据都存起来,当然小数据无所谓,但如果达到几十万,几百万的时候,可想多浪费空间,而且每次调整更是要漫长多了。



lucene实现的PriorityQueue(以最小堆来实现top k)

它是一个不变长的优先级队列.当队列达到指定最大size的时候,并不会扩充,top k外的数据都会抛掉,这样不仅省空间,也因为轻便调整这个堆 时可以更快。一般来说我要一个top k的数据,直觉想到的是使用最大堆,但这里它不是使用最大堆 ,而是使用最小堆来维护top k的数据。最后在输出的时候,先吐出来的时候倒一下序就可以了。


先看一下数据加入队列的时候的代码:


 public T insertWithOverflow(T element) {
    //堆未满的时候
    if (size < maxSize) {
      add(element);
      return null;
    }
    //新元素跟堆顶值比较,如果比堆顶值大,则进行调整
    else if (size > 0 && !lessThan(element, heap[1])) {
      T ret = heap[1];
      heap[1] = element;
      updateTop();
      return ret;
    } 
    //否则不需要调整原堆
    else {
      return element;
    }
  }




这样当堆满的时候,每次新来一个数据只需要跟堆顶比较就可以了,比堆顶小的数据都可以抛掉,比堆顶大的数就将堆顶抛掉,将新数据加到这个最小堆,然后再调整最小堆 .之后以此类推,最后这个堆里的数据就是我们要的top k的数据。。



当加入新元素时,此时堆未满,则从下往上调整


  private final void upHeap() {
    int i = size;
    T node = heap[i];			  // save bottom node
    int j = i >>> 1;
    while (j > 0 && lessThan(node, heap[j])) {
      heap[i] = heap[j];			  // shift parents down
      i = j;
      j = j >>> 1;
    }
    heap[i] = node;				  // install saved node
  }



当堆满 的时候,从下往上调整,从(n/2)的位置开始,保持最小堆的定义,heap[1]保存最小堆的元素


 private final void downHeap() {
    int i = 1;
    T node = heap[i];			  // save top node
    int j = i << 1;				  // find smaller child
    int k = j + 1;
    if (k <= size && lessThan(heap[k], heap[j])) {
      j = k;
    }
    while (j <= size && lessThan(heap[j], node)) {
      heap[i] = heap[j];			  // shift up child
      i = j;
      j = i << 1;
      k = j + 1;
      if (k <= size && lessThan(heap[k], heap[j])) {
        j = k;
      }
    }
    heap[i] = node;				  // install saved node
  }


最后最小堆推出数据,每次吐出这个最小堆里最小的数据,

  public final T pop() {
    if (size > 0) {
      T result = heap[1];			  // save first value
      heap[1] = heap[size];			  // move last to first
      heap[size] = null;			  // permit GC of objects
      size--;
      downHeap();				  // adjust heap
      return result;
    } else
      return null;
  }

所以一般是用一个数组从后往前填写,这样就得到一个top k的列表

 for (int i = howMany - 1; i >= 0; i--) { 
      results[i] = pq.pop();
    }







  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值