一、概念

      优先队列(priorityQueue)即堆(Heap),用于将队列中某个符合条件(比如最小)的元素优先排在队首。

 

二、操作

      基本的实现基于二叉堆(非叶子节点总是小于其子节点且根节点是最小值的完全二叉树),堆的操作主要有insert与delete。二叉堆实际上是用数组来存储数据的,且数组第二个元素开始才是队首。

                                  13

                            /              \

                       21                   16

                   /         \             /     \

               24            31       19      68

             /     \         /

           65     26    32

 

      下标:                   0   1   2    3    4    5    6    7   8    9   10 11 …

      实际存储的数组为:{_, 13, 21, 16, 24, 31, 19, 68, 65, 26, 32, _, ……}

     

      1.insert

      首先在最末叶子节点的下一个位置构造一个空节点;然后让所插入元素与空节点的父节点比较,如果父节点小于所插入元素则在空节点中存入所插入的值完成操作,否则把父节点的值存入空节点,并将空节点往上层冒。逐层比较直到比较到根节点为止。 

      例:插入14

                     13                                                         13                                                           13                                                           13

              /              \                                             /              \                                              /              \                                               /              \

           21                   16                                   21                   16                                    21                  16                                    14                   16

       /         \             /     \            —>             /        \              /     \             —>             /        \             /     \            —>             /         \             /     \

     24            31     19      68                      24            31       19      68                     24            O       19      68                        24            21       19      68

   /     \         /                                          /     \        /    \                                        /     \         /    \                                        /     \         /    \

65     26    32                                        65     26    32     O                               65      26    32     31                                  65      26    32     31

 

      2.delete

      删除操作通常是删除并返回队首元素,首先在根节点构造一个空节点并保存最末叶子节点元素(该节点已经排除在堆实际保存的数据之外,只是在数组中还保留着其数据,不会对后续操作造成影响)的值,然后将根节点较小的那个子节点的元素存入根节点中并将空节点往下冒,接着对该较小的节点执行与根节点同样的操作,直到之前保存的元素可以存放到当前空节点中(空节点为最底层叶子节点,或者之前保存值等于当前空节点的某个子节点)。

      例:接着上例执行delete,保存值为31

                     O                                                          14                                                           14                                                           14

              /              \                                             /              \                                              /              \                                               /              \

           14                   16                                    O                   16                                    21                  16                                    21                   16

       /         \             /     \            —>             /        \              /     \             —>             /        \             /     \            —>             /         \             /     \

     24            21     19      68                      24            21       19      68                     24            O       19      68                         24          31      19      68

   /     \         /    \                                      /     \         /    \                                       /     \         /    \                                        /     \        /    \

65     26    32      31                               65     26    32     31                              65      26    32     31                                  65     26   32     31

 

三、实现

public class BinaryHeap<E extends Comparable<? super E>> {

    public BinaryHeap() {
        this(DEFAULT_CAPACITY);
    }
    public BinaryHeap(int capacity) {
        currentSize = 0;
        arr = new Comparable[capacity + 1];
    }
    public BinaryHeap(E[] items) {
        currentSize = items.length;
        arr = new Comparable[(currentSize + 2) * 11 / 10];
        
        int i = 1;
        for(E item : items)
            arr[i] = item;
        buildHeap();
    }
    
    public void insert(E x) {
        if(currentSize == arr.length - 1) 
            enlargeArr(arr.length * 2 + 1);
        
        int hole = ++currentSize;
        for(; hole > 1 && x.compareTo((E) arr[hole / 2]) < 0; hole /= 2)
            arr[hole] = arr[hole/2];
        arr[hole] = x;
    }
    public E findMin() {
        if(isEmpty())
            return null;
        return (E)arr[1];
    }
    public E deleteMin() throws Exception {
        if(isEmpty()) 
            throw new Exception();
        
        E minItem = findMin();
        arr[1] = arr[currentSize--];
        percolateDown(1);
        
        return minItem;
        
    }
    public boolean isEmpty() {
        return currentSize == 0;
    }
    public void makeEmpty() {
        currentSize = 0;
    }
    
    private static final int DEFAULT_CAPACITY = 15;
    private int currentSize;
    private Comparable[] arr;
    
    private void  percolateDown(int hole) {
        int child;
        E tmp = (E) arr[hole];
        for( ; hole * 2 <= currentSize; hole = child) {
            child = hole * 2;
            if(child != currentSize && arr[child + 1].compareTo(arr[child]) < 0)
                child++;
            if(arr[child].compareTo(tmp) < 0)
                arr[hole] = arr[child];
            else
                break;
        }
        arr[hole] = tmp;
    }
    private void buildHeap() {
        for(int i = currentSize / 2; i > 0; i--)
            percolateDown(i);
    }
    private void enlargeArr(int size) {
        Comparable[] tmp = arr;
        arr = new Comparable[tmp.length + tmp.length / 2];
        for(int i = 0; i < tmp.length; i++)
            arr[i] = tmp[i];
    }
}