priority_queue默认大根堆,改小根堆需自定义比较器:仿函数中写a>b(定义“小于”语义),且operator()必须为const成员函数并用const引用参数;lambda须通过CTAD传递,不可直接作模板参数。

priority_queue 默认是大根堆,想改成小根堆得反着写比较逻辑
很多人一上来就写 std::greater,确实能搞定基础类型的小根堆,但自定义类型或复杂排序时,std::greater 用不了——它只支持内置类型且要求重载了 operator>。真正可控的方式是传一个仿函数(functor),也就是重载了 operator() 的结构体或类。
关键点在于:priority_queue 的第三个模板参数是「比较器」,它判断「谁该排在前面」;而它的语义是“如果 comp(a, b) 为 true,说明 a 优先级低于 b,即 b 应该更靠近队首”。换句话说,这个比较器实际定义的是「小于关系」(a 小于 b → a 该沉下去),所以你写的逻辑要和直觉的“排序顺序”反过来。
- 要实现小根堆?仿函数里写
a > b - 要按某个成员升序?写
a.val > b.val - 要先按
score降序、再按id升序?写a.score != b.score ? a.score b.id
仿函数必须是 const 成员函数,且参数用 const 引用避免拷贝
写 struct 时容易漏掉 const 限定符,导致编译失败,错误信息通常是类似 no match for call to ... (const MyComp)(const T&, const T&)。这是因为 priority_queue 内部调用比较器时传的是 const 对象,你的 operator() 也得是 const 成员函数。
另外,如果元素类型较大(比如含 vector 或 string),传值会触发不必要的拷贝,性能受损。务必用 const 引用:
立即学习“C++免费学习笔记(深入)”;
struct Compare {
bool operator()(const Node& a, const Node& b) const {
return a.cost > b.cost; // 小根堆:cost 小的优先
}
};
std::priority_queue, Compare> pq;
lambda 不能直接当模板参数,得用 function 包装或推导类型
有人想写 auto cmp = [](const auto& a, const auto& b) { return a.x > b.x; }; 然后传进 priority_queue 模板——不行。lambda 类型是独有且不可名状的,无法作为模板实参。两种可行路径:
- 用
std::function包装(但有虚调用开销,不推荐用于高频场景) - 让编译器自动推导:C++17 起支持
std::priority_queue pq(cmp)这种类模板参数推导(CTAD),前提是把比较器作为构造函数参数传入,而不是模板参数
后者更常用也更高效:
auto cmp = [](const Node& a, const Node& b) { return a.dist > b.dist; };
std::priority_queue, decltype(cmp)> pq(cmp);
operator
如果你的结构体自己写了 operator,但 priority_queue 又传了另一个仿函数,别指望它自动识别或报错——它完全忽略 operator,只认你模板里给的第三个参数。这点容易被忽略,尤其当调试时发现排序不对,回头检查才发现仿函数写反了,或者误以为 operator 起作用了。
更隐蔽的坑是:如果用了默认模板参数(只写前两个),比如 std::priority_queue,那它会尝试用 std::less,而这又依赖 Node::operator。一旦没定义,编译失败;一旦定义了但语义和你想要的不一致(比如你其实想要小根堆),结果就和预期相反。
所以最稳妥的做法是:永远显式写出三个模板参数,哪怕只是 std::less,也比靠默认行为强。









