一,堆的介绍和常见用法
堆又可称之为完全二叉堆。这是一个逻辑上基于完全二叉树、物理上一般基于线性数据结构(如数组、向量、链表等)的一种数据结构。
堆又分成大根堆和小根堆。大根堆即根节点大于叶子节点,下面的父节点也比孩子节点大。小根堆与之相反即根节点小于叶子节点,下面的父节点也比孩子节点小。
根堆的最常见的用法是获取数组的topN个数据。比如可以通过大根堆来得到数组中topK小的数据,或者小根堆获取数组中topk大的数据。
以大根堆来得到数组中topK小的数据来讲解,数组中前k个数据直接进堆构造成量级为k的大根堆,对于k个之后的数据,进堆前要判断是不是比堆顶元素要小,如果不是就不动,如果比堆顶小就把堆顶元素弹出然后数据进堆(堆会重构成大根堆),这样就相当于把堆中的最大数替换成较小的值了。这样一直替换下去最后堆中保存的就是数组topk个最小值了。
相似的数组中topk最大值通过小根堆来操作,原理一样。
二,Java中堆的使用
Java中堆是PriorityQueue 实现的。Java PriorityQueue实现了Queue 接口,不允许放入 null 元素。
默认是小根堆,要定义大根堆的话需要在定义式实现comparator类,重写compare函数。
常用方法总结:
public boolean add(E e); //在队尾插入元素,插入失败时抛出异常,并调整堆结构
public boolean offer(E e); //在队尾插入元素,插入失败时抛出false,并调整堆结构
public E remove(); //获取队头元素并删除,并返回,失败时前者抛出异常,再调整堆结构
public E poll(); //获取队头元素并删除,并返回,失败时前者抛出null,再调整堆结构
public E element(); //返回队头元素(不删除),失败时前者抛出异常
public E peek();//返回队头元素(不删除),失败时前者抛出null
public boolean isEmpty(); //判断队列是否为空
public int size(); //获取队列中元素个数
public void clear(); //清空队列
public boolean contains(Object o); //判断队列中是否包含指定元素(从队头到队尾遍历)
public Iterator<E> iterator(); //迭代器
TopK的问题:
public int[] inventoryManagement(int[] stock, int cnt) {
//使用大根堆来获取topk的最小值,大根堆即根节点大于叶子节点,构造完之后每次数据与堆顶元素相比比堆顶大就进堆。
//这样就把堆中最大元素替换成了较小值,这样最后堆中存储的就是k个最小值了。
int[] res = new int[cnt];
if(cnt==0)
return res;
//构造大根堆,实现compare函数。
PriorityQueue<Integer> pqueue = new PriorityQueue<Integer>(new Comparator<Integer>() {
public int compare(Integer num1,Integer num2){
return num2 - num1;
}
});
for(int i=0;i<stock.length;i++){
if(i<cnt)
pqueue.offer(stock[i]);
else{
int topnum = pqueue.peek();
if(stock[i] < topnum){
pqueue.poll();
pqueue.offer(stock[i]);
}
}
}
for(int i=0;i<cnt;i++){
res[i] = pqueue.poll();
}
return res;
}