C++ STL 学生与竞赛选手都会遇到的疑惑。
下面我们系统讲清楚:
🧩 一、结论速览表:STL 常见数据结构默认排序方向
容器类型 | 默认排序方向 | 底层实现 | 比较器默认类型 | 改为从大到小的写法 |
---|---|---|---|---|
std::set |
从小到大(升序) | 红黑树 | std::less<T> |
std::set<T, std::greater<T>> s; |
std::map |
从小到大(key 升序) | 红黑树 | std::less<Key> |
std::map<Key, Value, std::greater<Key>> m; |
std::multiset |
从小到大(升序) | 红黑树 | std::less<T> |
std::multiset<T, std::greater<T>> ms; |
std::priority_queue |
从大到小(最大堆) | 堆(vector+heap) | std::less<T> |
std::priority_queue<T, vector<T>, std::greater<T>> pq; |
std::sort |
从小到大(升序) | 快速排序 | std::less<T> |
std::sort(a.begin(), a.end(), greater<T>()); |
std::unordered_set / map |
无序(hash) | 哈希表 | N/A | 不支持排序 |
std::queue / stack |
无序 | 顺序容器封装 | N/A | 不支持排序 |
🧠 二、为什么 set 默认是升序,而 priority_queue 默认是降序?
(1)set
/ map
升序的逻辑:
- 它们的底层是红黑树(平衡二叉搜索树)。
- 为了保证查找/迭代的逻辑性,默认让中序遍历结果是从小到大。
- 所以
set<int> s;
中,s.begin()
指向最小值,s.rbegin()
才是最大值。
➡️ 优点:
- 有序遍历自然是升序;
- 查找上界 / 下界(
upper_bound
,lower_bound
)逻辑统一。
(2)priority_queue
默认是最大堆(降序输出)的逻辑:
- 底层是
vector + make_heap / push_heap / pop_heap
实现的二叉堆。 - 默认比较器
less<T>
表示「大的优先级更高」。 - 所以
pq.top()
永远返回当前最大元素。
➡️ 优点:
- 更符合“优先队列”语义:优先取出“最大优先级”元素;
- 典型应用如 Dijkstra、Huffman、Top-K 问题等。
⚙️ 三、如果要改为从大到小 / 从小到大,怎么写?
✅ 1️⃣ set
/ map
set<int, greater<int>> s; // 从大到小
map<int, string, greater<int>> m; // key 从大到小
✅ 2️⃣ priority_queue
默认:
priority_queue<int> pq; // 大根堆(最大值优先)
改为小根堆:
priority_queue<int, vector<int>, greater<int>> pq; // 小根堆(最小值优先)
✅ 3️⃣ sort
排序
sort(v.begin(), v.end()); // 从小到大
sort(v.begin(), v.end(), greater<>());// 从大到小
✅ 4️⃣ multiset
multiset<int, greater<int>> ms; // 从大到小排序
📦 四、举个完整例子:set 与 priority_queue 对比演示
#include <bits/stdc++.h>
using namespace std;int main() {set<int> s1 = {3, 1, 4, 1, 5};set<int, greater<int>> s2 = {3, 1, 4, 1, 5};priority_queue<int> pq1; // 大根堆priority_queue<int, vector<int>, greater<int>> pq2; // 小根堆for (int x : {3, 1, 4, 1, 5}) pq1.push(x), pq2.push(x);cout << "set升序: ";for (int x : s1) cout << x << " "; // 1 3 4 5cout << "\nset降序: ";for (int x : s2) cout << x << " "; // 5 4 3 1cout << "\n大根堆: ";while(!pq1.empty()){ cout << pq1.top() << " "; pq1.pop(); } // 5 4 3 1 1cout << "\n小根堆: ";while(!pq2.empty()){ cout << pq2.top() << " "; pq2.pop(); } // 1 1 3 4 5
}
💡 五、总结口诀(记忆法)
容器 | 默认方向 | 记忆口诀 |
---|---|---|
set / map |
升序 | 树按中序 → 小到大 |
priority_queue |
降序(最大堆) | 优先取“大”的 → 最大堆 |
sort |
升序 | 从小排到大 |
改方向 | 用 greater<T> |
比较器模板换一下就行 |
记忆技巧
- 关联容器(set/map):
greater<T>
实现从大到小 - 优先队列:
greater<T>
实现小根堆(从小到大) - 算法函数:
greater<T>()
实现从大到小排序
记住 greater
总是产生"更严格的"排序规则。