优先级队列

优先级队列使用二叉堆实现的

插入元素和删除队列中最大的元素时间复杂度都是O(logn),因为这俩个操作是基于二叉堆的上浮和下沉操作实现的

二叉堆是什么?

二叉堆逻辑结构是完全二叉树,存储结构是数组

二叉堆是怎么存的呢?

// 父节点的索引
int parent(int root) {
    return root / 2;
}
// 左孩子的索引
int left(int root) {
    return root * 2;
}
// 右孩子的索引
int right(int root) {
    return root * 2 + 1;
}

如图:数组索引0的位置专门置空

优先级队列的实现

public class MaxPQ {
    private Integer[] pq;
    // 当前 Priority Queue 中的元素个数
    private int N = 0;

    public MaxPQ(int cap) {
        // 索引 0 不用,所以多分配一个空间
        pq = new Integer[cap + 1];
    }

    public static void main(String[] args) {
        MaxPQ pq=new MaxPQ(5);
        pq.insert(5);
        pq.insert(4);
        pq.insert(3);

        System.out.println(pq.max());//5
        pq.delMax();
        System.out.println(pq.max());//4
        pq.insert(10);
        System.out.println(pq.max());//10

    }



    /* 返回当前队列中最大元素 */
    public int max() {
        return pq[1];
    }

    /* 插入元素 e */
    public void insert(int e) {
        N++;
        // 先把新元素加到最后
        pq[N] = e;
        // 然后让它上浮到正确的位置
        swim(N);

    }

    /* 删除并返回当前队列中最大元素 */
    public int delMax() {
        // 最大堆的堆顶就是最大元素
        int max = pq[1];
        // 把这个最大元素换到最后,删除之
        exch(1, N);
        pq[N] = null;
        N--;
        // 让 pq[1] 下沉到正确位置
        sink(1);
        return max;
    }

    /* 上浮第 k 个元素,以维护最大堆性质 */
    private void swim(int k) {
        // 如果浮到堆顶,就不能再上浮了
        while (k > 1 && less(parent(k), k)) {
            // 如果第 k 个元素比上层大
            // 将 k 换上去
            exch(parent(k), k);
            k = parent(k);
        }

    }

    /* 下沉第 k 个元素,以维护最大堆性质 */
    private void sink(int k) {
        // 如果沉到堆底,就沉不下去了
        while (left(k) <= N) {
            // 先假设左边节点较大
            int older = left(k);
            // 如果右边节点存在,比一下大小
            if (right(k) <= N && less(older, right(k)))
                older = right(k);
            // 结点 k 比俩孩子都大,就不必下沉了
            if (less(older, k)) break;
            // 否则,不符合最大堆的结构,下沉 k 结点
            exch(k, older);
            k = older;
        }

    }

    /* 交换数组的两个元素 */
    private void exch(int i, int j) {
        int temp = pq[i];
        pq[i] = pq[j];
        pq[j] = temp;
    }

    /* pq[i] 是否比 pq[j] 小? */
    private boolean less(int i, int j) {
        return pq[i] < pq[j];
    }
    // 父节点的索引
    int parent(int root) {
        return root / 2;
    }
    // 左孩子的索引
    int left(int root) {
        return root * 2;
    }
    // 右孩子的索引
    int right(int root) {
        return root * 2 + 1;
    }

}

总结

  • 二叉堆就是一种完全二叉树,所以适合存储在数组中,而且二叉堆拥有一些特殊性质。

  • 二叉堆的操作很简单,主要就是上浮和下沉,来维护堆的性质(堆有序),核心代码也就十行。

  • 优先级队列是基于二叉堆实现的,主要操作是插入和删除。插入是先插到最后,然后上浮到正确位置;删除是调换位置后再删除,然后下沉到正确位置。核心代码也就十行。

posted @ 2020-04-22 16:18  程序员小宇  阅读(180)  评论(0编辑  收藏  举报