优先队列是一种按照键值出队的队列,而不是像普通队列一样先进先出,后进后出。优先队列适合解决需要迅速找到找到最大值最小值的问题。 如果键值越小优先级越高,那么叫作升序优先队列;如果键值越大优先级越高则叫做降序优先队列。
用堆来实现优先队列,首先给出堆具有的属性和基础方法。
public class max_heap extends Comparable<E>>{ private Array<E> data; public max_heap(int len){ data = new Array<>(len); } public max_heap(){ data = new Array<>(); } public int size(){ return data.getSize(); } public boolean isEmpty(){ return data.isEmpty(); } private int parent(int index){ if(index == 0) throw new IllegalArgumentException("index 0 has not parent"); return (index-1) / 2; } private int left_child(int index){ return index*2 + 1; } private int right_child(int index){ return index*2 - 1; } private void swap(int i, int j){ int temp = data.get(i); data.set(i, data.get(j)); data.set(j, temp); } }向堆添加元素时,首先新建一个节点放入该元素,然后再将它与其父节点对比,如果小于(或大于)其父节点,则将该节点上移,继续判断,一直到满足规定为止
public void add(E e){ data.addLast(e); sift_up(data.getSize()-1); } private sift_up(int index){ while(index > 0 && data.get(parent(index)).compareTo(index) < 0){ swap(index, parent(index)); index = parent(index); } }由于堆的特性,在出队时,堆顶的元素即是我们需要的元素,所以出队操作要返回堆顶元素,并且删去该元素,然后重新调整堆。在调整堆时,我们一般将末尾元素移至堆首,然后自上而下进行调整,代码如下
public E get_max(){ E res = data.get(0); swap(0, data.getSize() - 1); data.removeLast(); sift_down(0); return res; } private void sift_down(int index){ int k = index; while(index < data.getSize()){ int k = left_child(index); if(k + 1 < data.getSize() && data.get(k).compareTo(data.get(k + 1))< 0) k++; if(data.get(index).compareTo(data.get(k)) < 0){ swap(k,index); index = k; } else break; }新建一个堆时只需要从最后一个非叶子节点开始向上一直做sift_down操作即可,最后一个非叶子节点就是数组最后一个数字的父亲节点。
public max_heap(E[] array){ for(int i = parent(array.getSize() - 1); i >=0; i--){ sift_down(i); }基于堆的优先队列
public class priority_que<E extends Comparable<E>> implements Queue<E>{ private max_heap<E> max_heap; public priority_que(){ max_heap = new max_heap(); } @Override public int getSize(){ return max_heap.getSize(); } @Override public boolean isEmpty(){ return max_heap.isEmpty(); } @Override public E getFront(){ return max_heap.find_max; } @Override public void enque(E e){ max_heap.add(e); } @Override public E deque(){ return max_heap.get_max(); } }基于二叉堆的优先队列入队出队都需要遍历整个完全二叉树的深度,故时间复杂度为O(logn),查看最大元素只需要访问堆首的元素,故时间复杂度为O(1)