二叉堆详解实现优先级队列

raw/articles/2026/05/二叉堆详解实现优先级队列.md

一句话总结

通过 swim(上浮)和 sink(下沉)两个核心操作维护堆性质,实现高效的优先级队列数据结构。

核心要点

1. 二叉堆的本质

二叉堆是一种特殊的完全二叉树,存储在数组中而非链式结构。通过数组索引计算父子关系:

  • 父节点:parent = root / 2
  • 左子节点:left = root * 2
  • 右子节点:right = root * 2 + 1

2. 堆性质

  • 最大堆:每个节点都大于等于它的两个子节点,堆顶是最大元素
  • 最小堆:每个节点都小于等于它的两个子节点,堆顶是最小元素

3. 两大核心操作

  • swim(上浮):当节点比父节点大时,交换位置向上浮动
  • sink(下沉):当节点比子节点小时,与较大的子节点交换向下沉

4. 优先级队列实现

基于二叉堆实现,核心 API:

  • insert(Key e):插入到堆底,然后 swim 上浮到正确位置
  • delMax():堆顶与堆底交换,删除原堆顶,然后 sink 下沉恢复堆性质

基本原理

二叉堆通过数组存储完全二叉树,利用索引关系而非指针维护树结构。插入和删除时通过局部调整(上浮/下沉)维护堆性质,避免全局重建。

上浮代码(swim)

private void swim(int k) {
    while (k > 1 && less(parent(k), k)) {
        exch(parent(k), k);
        k = parent(k);
    }
}

下沉代码(sink)

private void sink(int k) {
    while (left(k) <= N) {
        int older = left(k);
        if (right(k) <= N && less(older, right(k)))
            older = right(k);
        if (less(older, k)) break;
        exch(k, older);
        k = older;
    }
}
概念 说明
完全二叉树 除最后一层外,所有层都满的二叉树
最大堆 父节点 ≥ 子节点的堆
最小堆 父节点 ≤ 子节点的堆
上浮/下沉 维护堆性质的局部调整操作

时间复杂度

操作 时间复杂度 说明
insert O(logK) K 为堆中元素总数,最多上浮树高次
delMax O(logK) 最多下沉树高次
max O(1) 直接返回堆顶元素

代码实现

关键代码片段

public class MaxPQ<Key extends Comparable<Key>> {
    private Key[] pq;
    private int N = 0;

    public MaxPQ(int cap) {
        pq = (Key[]) new Comparable[cap + 1];
    }

    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--;
        sink(1);
        return max;
    }
}

实现要点

  • 数组索引 0 空着不用,从 1 开始存储,简化父子关系计算
  • swim 和 sink 是互逆操作,但应用场景不同(插入用上浮,删除用下沉)
  • 下沉时需要比较左右子节点,选择较大的进行交换

应用场景

  • 堆排序(Heap Sort)
  • 优先级队列(Priority Queue)
  • 任务调度系统
  • Dijkstra 等图算法中的优先队列优化
  • TopK 问题(维护大小为 K 的堆)

相关概念

相关实体

  • labuladong:本文作者,算法博主
  • 博客园:文章发布平台,暂不创建独立页

总结

二叉堆是一种简单而强大的数据结构,通过 swim 和 sink 两个操作(代码仅十行)就能维护堆性质,进而实现高效的优先级队列。其巧妙之处在于用数组存储完全二叉树,通过索引计算而非指针维护父子关系,体现了数据结构设计的精妙。


Interactive Graph