堆总结

二叉堆就是一种完全二叉树,存储在数组中,用下标表示位置

操作很简单,主要就是上浮下沉,来维护堆的性质(堆有序)

优先级队列

是基于二叉堆实现的,主要操作是插入和删除。插入是先插到最后,然后上浮到正确位置;删除是删堆顶,先将堆顶与堆底调换位置后再删除,然后新堆顶下沉到正确位置。(直接删顶要考虑维护堆性质,而直接删底不用,因此采用交换后删除的方式)

时间复杂度

插入和删除元素的时间复杂度为 O(logK)K 为当前二叉堆(优先级队列)中的元素总数。因为我们时间复杂度主要花费在 sink 或者 swim 上,而不管上浮还是下沉,最多也就树(堆)的高度,也就是 log 级别。

具体实现

优先级队列数据结构

public class MaxPQ
    <Key extends Comparable<Key>> {
    // 存储元素的数组
    private Key[] pq;
    // 当前 Priority Queue 中的元素个数
    private int N = 0;

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

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

    /* 插入元素 e */
    public void insert(Key e) {...}

    /* 删除并返回当前队列中最大元素 */
    public Key delMax() {...}

    /* 上浮第 k 个元素,以维护最大堆性质 */
    private void swim(int k) {...}

    /* 下沉第 k 个元素,以维护最大堆性质 */
    private void sink(int k) {...}

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

    /* pq[i] 是否比 pq[j] 小? */
    private boolean less(int i, int j) {
        return pq[i].compareTo(pq[j]) < 0;
    }

    /* 还有 left, right, parent 三个方法 */
}

插入

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

删除

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

上浮

private void swim(int k) {
    // 如果浮到堆顶,就不能再上浮了
    while (k > 1 && less(parent(k), k)) {
        // 如果第 k 个元素比上层大
        // 将 k 换上去
        exch(parent(k), k);
        k = parent(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;
    }
}

python大根堆

py没有大根堆。存入负权值,用小根堆实现大根堆

class BigHeap:
    def __init__(self):
        self.arr = list()
    #建堆
    def heapify(self):
        heapq.heapify(self.arr)
    #插入
    def insert(self, val):
        heapq.heappush(self.arr, -val)   # 存入负值
    #弹出
    def pop(self):
        val = heapq.heappop(self.arr)
        # 先取出再转负
        return -val
    #返回堆顶
    def getTop(self):
        if not self.arr:
            return
        return -self.arr[0]
    #判断堆空
    def isEmpty(self):
        return not self.arr
    #返回堆大小
    def size(self):
        return len(self.arr)

使用:

heap = BigHeap()	#创建
heap.insert(e)	#插入
e = heap.pop	#弹出

最后更新于