默认是大顶堆,因为priority_queue第三个模板参数默认为std::less,其comp(a,b)=a

默认就是大顶堆,小顶堆必须显式指定比较器,否则 priority_queue 永远不会变成最小堆。
为什么默认是大顶堆?
因为 priority_queue 的第三个模板参数默认是 less,它用 a 判断优先级:当 a 为真时,b 被认为“更大”、更优先,被推到堆顶。这等价于“最大值先出”。
常见误解是以为 operator 定义了“谁该排前面”,其实它定义的是“谁该被压在下面”——堆顶永远是那个在比较中“最不满足 关系”的元素。
- 写
struct Task { bool operator → 还是大顶堆(高 priority 先出) - 想让低 priority 先出?得反过来写:
return priority > rhs.priority;,或换用greater - 底层容器必须支持随机访问,
vector是默认且最常用选择;deque可用但极少必要;list直接编译报错
构建小顶堆的三种可靠方式
必须显式传入第三个模板参数,且类型要对。传对象(如 greater)会编译失败,只接受类型名。
-
priority_queue—— 最简洁安全,头文件需, greater > min_pq; -
struct GreaterInt { bool operator()(int a, int b) { return a > b; } }; priority_queue—— 适合带状态的复杂比较逻辑, GreaterInt> min_pq; - lambda 不可直接用作模板参数(类型不可名),但可通过
decltype绕过:auto cmp = [](int a, int b) { return a > b; }; priority_queue, decltype(cmp)> min_pq(cmp);
自定义结构体时最容易踩的坑
错误常出现在比较逻辑和 const 正确性上。比如高频题 LC 347 中统计频次后取 top-K,若定义 pair 存 {val, freq},小顶堆应按 freq 升序排,但写成 lhs.second 就会得到大顶堆。
- 务必确认比较函数返回
true时,第一个参数是否“应该排在第二个参数下面”(即优先级更低) - 成员函数必须加
const和noexcept(虽非强制,但避免隐式转换问题) - 若用
unordered_map查频次再进堆,注意 lambda 捕获时用引用([&freqMap]),否则捕获的是空副本 - 不要依赖
top()修改内容——top()返回的是 const 引用,强行 const_cast 修改会破坏堆序,后续pop()可能崩溃
真正麻烦的不是怎么写对,而是改 bug 时发现堆行为反直觉——因为 priority_queue 的“优先级”是反着定义的:它维护的是“谁该沉底”,而不是“谁该浮起”。盯住那一个 > 或 符号,比背口诀管用得多。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











