Files
Data-Structure/STL/STL-priority_queue/README.MD
2025-08-12 23:00:31 +08:00

422 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

好的!我来给你详细介绍一下 C++ STL 里的 `priority_queue` 容器适配器的用法和常见技巧,帮你全面掌握它。
---
# 一、什么是 `priority_queue`
`priority_queue` 是 STL 提供的一个容器适配器底层默认使用堆heap数据结构实现保证访问时总能快速取得最大默认或最小元素。
它类似于一个“优先级队列”,总是先弹出优先级最高(优先级依据元素大小)的元素。
---
# 二、包含头文件
```cpp
#include <queue>
```
`priority_queue` 位于 `<queue>` 头文件中。
---
# 三、定义和基本用法
```cpp
#include <iostream>
#include <queue>
int main() {
std::priority_queue<int> pq; // 默认大顶堆元素类型为int
pq.push(5);
pq.push(1);
pq.push(10);
pq.push(7);
// 访问优先级最高的元素(最大值)
std::cout << "Top: " << pq.top() << "\n"; // 10
// 弹出元素,优先级最高的先出
pq.pop();
std::cout << "Top after pop: " << pq.top() << "\n"; // 7
return 0;
}
```
---
# 四、构造函数(常见形式)
```cpp
priority_queue();
priority_queue(const Compare& comp); // 指定比较函数
priority_queue(const Compare& comp, const Container& cont);
priority_queue(const priority_queue& other);
```
---
# 五、成员函数
| 函数 | 说明 |
| ----------------------- | ------------- |
| `push(const T& val)` | 插入元素 |
| `pop()` | 删除堆顶元素 |
| `top()` | 返回堆顶元素(最大或最小) |
| `empty()` | 判断是否为空 |
| `size()` | 返回元素个数 |
| `emplace(args...)` | 原地构造并插入元素 |
| `swap(priority_queue&)` | 交换两个优先队列 |
---
# 六、默认是大顶堆,怎么做小顶堆?
默认情况下,`priority_queue` 采用 `std::less<T>`,即大顶堆。
**方法1用 `std::greater<T>`**
```cpp
std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;
minHeap.push(3);
minHeap.push(10);
minHeap.push(1);
std::cout << minHeap.top() << "\n"; // 1
```
**说明:**
* 第二个模板参数是底层容器,默认是 `std::vector<T>`
* 第三个模板参数是比较器,默认是 `std::less<T>`(大顶堆)
* 改成 `std::greater<T>` 就是小顶堆
---
# 七、自定义比较器
1. **函数对象**
```cpp
struct Compare {
bool operator()(int a, int b) {
return a > b; // 这里定义小顶堆
}
};
std::priority_queue<int, std::vector<int>, Compare> pq;
```
2. **Lambda 表达式C++11 及以上)**
```cpp
auto cmp = [](int left, int right) { return left > right; };
std::priority_queue<int, std::vector<int>, decltype(cmp)> pq(cmp);
pq.push(4);
pq.push(2);
pq.push(8);
std::cout << pq.top() << "\n"; // 2
```
---
# 八、优先队列存储自定义类型
假设你有一个结构体:
```cpp
struct Person {
std::string name;
int age;
};
// 按年龄从大到小排序(大顶堆)
struct ComparePerson {
bool operator()(const Person& a, const Person& b) {
return a.age < b.age; // 年龄大的优先级高
}
};
std::priority_queue<Person, std::vector<Person>, ComparePerson> pq;
```
使用:
```cpp
pq.push({"Alice", 30});
pq.push({"Bob", 25});
pq.push({"Cindy", 35});
auto top = pq.top();
std::cout << top.name << " " << top.age << "\n"; // Cindy 35
```
---
# 九、注意事项和技巧
* `priority_queue` 不能遍历和访问除堆顶以外的元素。
* 如果需要修改某个元素的优先级,通常做法是重新插入修改后的元素,旧元素保留或标记过期。
* `emplace()``push()` 直接传入构造参数,效率更高。
---
# 十、示例综合
```cpp
#include <iostream>
#include <queue>
#include <vector>
#include <string>
struct Task {
std::string name;
int priority;
};
struct TaskCmp {
bool operator()(const Task& a, const Task& b) {
return a.priority < b.priority; // 大顶堆
}
};
int main() {
std::priority_queue<Task, std::vector<Task>, TaskCmp> tasks;
tasks.emplace(Task{"Task1", 5});
tasks.push({"Task2", 1});
tasks.push({"Task3", 10});
while (!tasks.empty()) {
Task t = tasks.top();
std::cout << t.name << ": " << t.priority << "\n";
tasks.pop();
}
return 0;
}
```
输出:
```
Task3: 10
Task1: 5
Task2: 1
```
---
好的!这里给你整理一个**C++ STL `priority_queue` 的全面使用方法大全**,包含它的构造函数、所有成员函数(含继承自底层容器的限制),以及使用示例和细节说明。
---
# STL `priority_queue` 完整介绍
```cpp
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type>
> class priority_queue;
```
* `T`:元素类型
* `Container`:底层容器类型,必须满足随机访问迭代器,默认是 `std::vector<T>`
* `Compare`:排序规则,默认是 `std::less<T>`(大顶堆)
---
# 一、构造函数
| 函数原型 | 说明 |
| ------------------------------------------------------------- | --------------- |
| `priority_queue()` | 默认构造一个空优先队列 |
| `explicit priority_queue(const Compare& comp);` | 使用指定比较器构造空优先队列 |
| `priority_queue(const Compare& comp, const Container& cont);` | 指定比较器和底层容器,拷贝构造 |
| `priority_queue(const priority_queue& other);` | 拷贝构造 |
| `priority_queue(priority_queue&& other) noexcept;` | 移动构造C++11 |
| `priority_queue& operator=(const priority_queue& other);` | 拷贝赋值 |
| `priority_queue& operator=(priority_queue&& other) noexcept;` | 移动赋值C++11 |
---
# 二、成员函数
| 函数原型 | 说明 |
| ------------------------------------------------------- | ------------------ |
| `bool empty() const;` | 判断是否为空 |
| `size_type size() const;` | 返回元素个数 |
| `const_reference top() const;` | 返回堆顶元素(优先级最高元素) |
| `void push(const value_type& val);` | 插入元素 |
| `void push(value_type&& val);` | 插入右值元素移动语义C++11 |
| `template<class... Args> void emplace(Args&&... args);` | 原地构造插入元素C++11 |
| `void pop();` | 弹出堆顶元素 |
| `void swap(priority_queue& other) noexcept;` | 交换两个优先队列 |
| `~priority_queue();` | 析构函数 |
---
# 三、底层容器访问protected
* `priority_queue` 不提供直接访问底层容器的接口,但它有一个受保护成员:
```cpp
protected:
Container c;
```
* 如果你继承 `priority_queue`,可以访问它。
---
# 四、成员变量说明
* `priority_queue` 内部维护了一个满足堆性质的容器 `c`,默认是 `vector<T>`
* 通过调用 `std::push_heap``std::pop_heap` 保持堆的有序结构。
---
# 五、示例代码
```cpp
#include <iostream>
#include <queue>
#include <vector>
#include <functional> // std::greater
int main() {
// 默认大顶堆
std::priority_queue<int> pq1;
pq1.push(5);
pq1.push(2);
pq1.push(8);
std::cout << "Max element: " << pq1.top() << "\n"; // 8
// 小顶堆,使用 greater 作为比较器
std::priority_queue<int, std::vector<int>, std::greater<int>> pq2;
pq2.push(5);
pq2.push(2);
pq2.push(8);
std::cout << "Min element: " << pq2.top() << "\n"; // 2
// emplace 直接构造
pq1.emplace(10);
std::cout << "Max element after emplace: " << pq1.top() << "\n"; // 10
// pop
pq1.pop();
std::cout << "Max element after pop: " << pq1.top() << "\n"; // 8
return 0;
}
```
---
# 六、使用自定义类型
```cpp
#include <iostream>
#include <queue>
#include <vector>
struct Person {
std::string name;
int age;
};
struct ComparePerson {
bool operator()(const Person& a, const Person& b) {
return a.age < b.age; // 按年龄降序(大顶堆)
}
};
int main() {
std::priority_queue<Person, std::vector<Person>, ComparePerson> pq;
pq.push({"Alice", 30});
pq.push({"Bob", 25});
pq.push({"Cindy", 35});
while (!pq.empty()) {
auto p = pq.top();
std::cout << p.name << " (" << p.age << ")\n";
pq.pop();
}
}
```
---
# 七、底层容器可以替换
默认是 `std::vector<T>`,也可以用 `std::deque<T>`,但必须支持随机访问迭代器。
```cpp
std::priority_queue<int, std::deque<int>> pq;
```
---
# 八、内部实现原理(简述)
* 底层容器是一个随机访问序列,使用 `std::push_heap``std::pop_heap` 来维护堆性质。
* `push()`:先插入底层容器尾部,再调用 `push_heap`
* `pop()`:调用 `pop_heap` 将最大元素交换到尾部,然后移除尾部元素。
* `top()`:返回底层容器的第一个元素,即堆顶元素。
---
# 九、完整成员函数接口C++标准)
```cpp
// 构造与析构
priority_queue();
explicit priority_queue(const Compare&);
priority_queue(const Compare&, const Container&);
priority_queue(const priority_queue&);
priority_queue(priority_queue&&) noexcept;
~priority_queue();
// 赋值
priority_queue& operator=(const priority_queue&);
priority_queue& operator=(priority_queue&&) noexcept;
// 访问
bool empty() const;
size_type size() const;
const_reference top() const;
// 修改
void push(const value_type&);
void push(value_type&&);
template <class... Args>
void emplace(Args&&...);
void pop();
void swap(priority_queue&) noexcept;
```
---
# 十、注意点
* 只能访问堆顶元素,不能遍历优先队列中的所有元素。
* 如果需要修改队列中间元素的优先级,通常要重新插入,不能直接修改。
* `emplace` 可以避免不必要的复制,提高效率。
* 默认是大顶堆,如果要小顶堆,改比较器为 `std::greater` 或自定义。
---
如果你需要,我可以帮你写详细代码示例,或者帮你写自定义比较器的示例!