std::priority_queue默认为最大堆,使用std::less使父节点≥子节点;要实现最小堆必须显式指定std::greater作为比较器,其语义是comp(a,b)为真时a优先级低于b。

std::priority_queue 默认就是最大堆,不是最小堆
它底层用 std::vector 存储,靠 std::less<t></t> 比较——这意味着父节点总是 ≥ 子节点,弹出的是最大值。想让它弹出最小值,必须换比较器,不能只靠“反转逻辑”这种模糊说法。
用 std::greater 是最直接的最小堆写法
这是标准库预定义的函数对象,语义清晰、无额外开销、兼容所有可比较类型。
- 写法:
std::priority_queue<int std::vector>, std::greater<int>> min_heap;</int></int> - 注意三个模板参数顺序不能错:元素类型、容器类型、比较器类型
- 如果省略后两个参数(比如只写
std::priority_queue<int></int>),就回退到默认最大堆 -
std::greater<t></t>要求T支持>运算符;自定义类型需重载该运算符或提供特化
自定义比较器时,返回 true 表示“应该下沉”,别搞反了
priority_queue 的比较器语义是:如果 comp(a, b) == true,则 a 在堆中**优先级低于** b(即 a 会被压得更深)。所以最小堆要让小的数“优先级高”,就得让 comp(a, b) 在 a > b 时返回 true。
- 错误写法(以为“反转”就是取反):
[](int a, int b) { return !(a —— 这等价于 <code>a >= b,不满足严格弱序,可能崩溃 - 正确写法(对应
std::greater):[](int a, int b) { return a > b; } - Lambda 必须是无捕获的,否则无法作为模板参数;若需捕获,得用
std::function+ 动态分配,但会损失性能且不能作为模板实参
pop() 和 top() 的时间复杂度没变,但构造和内存布局有隐含成本
改用 std::greater 或自定义比较器不会影响单次 push()/pop() 的 O(log n) 复杂度,但要注意:
立即学习“C++免费学习笔记(深入)”;
- 显式写出三个模板参数后,类型变长,可能影响模板推导(比如用
auto声明时需配合decltype) - 如果容器类型不是
std::vector(比如换成std::deque),虽合法但缓存局部性变差,实际性能可能下降 - 对浮点数用
std::greater<double></double>要小心 NaN:NaN > anything 是 false,可能导致堆结构异常,建议先过滤或封装比较逻辑
最小堆的关键不在“怎么反转”,而在理解 priority_queue 的比较器控制的是“谁该被埋得更深”。std::greater 是最稳的选择;手写比较器时,盯着 a > b 写,别碰 ! 和 。









