std::priority_queue (优先队列) 用法简析

在 STL 中,std::priority_queue 是一个非常有用的容器适配器,它允许我们快速访问队列中“优先级最高”的元素。它通常被实现为“堆”(Heap)数据结构。

std::priority_queue 对于需要动态查找最大值或最小值的场景(例如任务调度、Dijkstra 算法等)非常高效。

std::priority_queue 的基本信息

  • 头文件: #include <queue>

  • 作用: 一个容器适配器,提供常数时间(O(1))查找最大(默认)或最小元素,插入和删除操作的时间复杂度为对数时间(O(log n))。

  • 特点:

    • 默认行为(最大堆): 默认情况下,top() 成员函数返回的是队列中最大的元素。

    • 非先进先出: 它不是一个标准的 FIFO 队列。元素的弹出顺序取决于它们的优先级,而不是它们被插入的顺序。

    • 底层容器: 默认使用 std::vector 作为其底层存储容器,也可以指定为 std::deque

一、默认用法(最大堆)

这是 std::priority_queue 最基本的用法。默认情况下,它使用 std::less<T> 作为比较器,这会构建一个“最大堆”(Max-Heap)。

这意味着 top() 总是返回当前队列中值最大的元素。

核心成员函数:

  • push(x): 插入元素 x 并自动维护堆的性质。

  • pop(): 移除 top() 元素(即优先级最高的元素)。

  • top(): 访问 top() 元素(不移除)。

  • empty(): 检查队列是否为空。

  • size(): 返回队列中元素的数量。

#include <iostream>
#include <queue>
#include <vector>

int main() {
    // 默认是最大堆
    // 模板参数为:<元素类型, 底层容器类型, 比较器类型>
    // 默认即: std::priority_queue<int, std::vector<int>, std::less<int>>
    std::priority_queue<int> max_heap;

    max_heap.push(30);
    max_heap.push(100);
    max_heap.push(20);
    max_heap.push(50);

    std::cout << "最大堆 (默认):" << std::endl;
    while (!max_heap.empty()) {
        // 访问顶部元素 (最大值)
        std::cout << max_heap.top() << " ";
        // 移除顶部元素
        max_heap.pop();
    }
    // 输出: 100 50 30 20
    std::cout << std::endl;
}

二、自定义排序(最小堆)

std::priority_queue 的灵活性在于它允许我们通过模板参数自定义比较逻辑,最常见的自定义就是实现一个“最小堆”(Min-Heap),即 top() 总是返回队列中最小的元素。

这是通过提供第三个模板参数——比较函数(Comparator)来实现的。

* 比较器逻辑:priority_queue 的比较器逻辑与 sort 相反。

  • 对于 sort(a, b, comp)comp(a, b) 返回 true 意味着 a 排在 b 前面

  • 对于 priority_queueCompare(a, b) 返回 true 意味着 a 的优先级低于 b(即 a 会被排在 b后面)。

  • 默认的 std::less<int> (a < b):如果 a < btrue,则 a 的优先级低,b(较大的)优先级高。因此得到最大堆

  • 要实现最小堆,我们需要 a > b 时返回 true,让 a(较大的)的优先级变低。这时我们使用 std::greater<int>

方式 1:使用 STL 内置比较器(最小堆)

#include <iostream>
#include <queue>
#include <vector>
#include <functional> // 包含 std::greater

int main() {
    // 1. 元素类型: int
    // 2. 底层容器: std::vector<int>
    // 3. 比较器: std::greater<int> (这会构建一个最小堆)
    std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;

    min_heap.push(30);
    min_heap.push(100);
    min_heap.push(20);
    min_heap.push(50);

    std::cout << "最小堆 (使用 std::greater):" << std::endl;
    while (!min_heap.empty()) {
        std::cout << min_heap.top() << " "; // 访问最小值
        min_heap.pop();
    }
    // 输出: 20 30 50 100
    std::cout << std::endl;
}

方式 2:使用自定义函数对象 (Functor)

#include <iostream>
#include <queue>
#include <vector>

// 比较函数对象:实现最小堆
struct CompareMin {
    // 当 l > r 时返回 true,意味着 l 的优先级低于 r
    bool operator()(int l, int r) const {
        return l > r;
    }
};

// 比较函数对象:实现最大堆 (同默认)
struct CompareMax {
    // 当 l < r 时返回 true,意味着 l 的优先级低于 r
    bool operator()(int l, int r) const {
        return l < r;
    }
};

int main() {
    std::priority_queue<int, std::vector<int>, CompareMin> min_heap_custom;
    
    min_heap_custom.push(30);
    min_heap_custom.push(100);
    min_heap_custom.push(20);

    std::cout << "最小堆 (使用自定义 Functor):" << std::endl;
    while (!min_heap_custom.empty()) {
        std::cout << min_heap_custom.top() << " "; // 输出: 20 30 100
        min_heap_custom.pop();
    }
    std::cout << std::endl;
}

方式 3:使用 Lambda 表达式(C++11 起)

使用 Lambda 表达式比 sort 要稍微复杂一点,因为比较器是模板参数,它需要一个“类型”。我们必须使用 decltype 来获取 Lambda 的类型。

#include <iostream>
#include <queue>
#include <vector>

int main() {
    // 定义 Lambda 比较器(实现最小堆)
    auto compare_min_lambda = [](int l, int r) {
        return l > r;
    };

    // 使用 decltype 获取 Lambda 的类型
    using MinHeapType = std::priority_queue<int, std::vector<int>, decltype(compare_min_lambda)>;
    
    // 构造优先队列时,需要传入 Lambda 对象实例
    MinHeapType min_heap_lambda(compare_min_lambda);

    min_heap_lambda.push(30);
    min_heap_lambda.push(100);
    min_heap_lambda.push(20);

    std::cout << "最小堆 (使用 Lambda):" << std::endl;
    while (!min_heap_lambda.empty()) {
        std::cout << min_heap_lambda.top() << " "; // 输出: 20 30 100
        min_heap_lambda.pop();
    }
    std::cout << std::endl;
}

三、存储自定义结构体

priority_queue 中存储的是自定义结构体(如 struct Student)时,我们有两种方式来定义排序规则。

#include <iostream>
#include <queue>
#include <vector>
#include <string>

struct Student {
    std::string name;
    int score;
};

方式 1:重载 < 运算符(推荐用于默认堆)

如果为结构体 Student 重载了 operator<,那么默认的 std::priority_queue<Student>(使用 std::less<Student>)就会自动使用这个重载。

注意std::less 使用 operator<a < b 返回 true 会使 b(分数高的)优先级更高,因此这将创建一个按分数排序的最大堆

struct Student {
    std::string name;
    int score;

    // 重载 < 运算符
    // 当 this->score < other.score 时返回 true
    // 这将使分数 (score) 高的学生优先级更高 (最大堆)
    bool operator<(const Student& other) const {
        return this->score < other.score;
    }
};

int main() {
    // 默认使用 std::less<Student>,它会调用我们重载的 operator<
    std::priority_queue<Student> student_max_heap;
    
    student_max_heap.push({"李四", 88});
    student_max_heap.push({"王五", 100});
    student_max_heap.push({"张三", 95});

    std::cout << "Student 最大堆 (重载 <):" << std::endl;
    while (!student_max_heap.empty()) {
        std::cout << student_max_heap.top().name << ": " << student_max_heap.top().score << std::endl;
        student_max_heap.pop();
    }
    // 输出:
    // 王五: 100
    // 张三: 95
    // 李四: 88
}

方式 2:使用自定义比较器 (Functor)

struct Student {
    std::string name;
    int score;
};

// 自定义比较器:按分数实现最小堆
struct CompareStudentMin {
    bool operator()(const Student& a, const Student& b) const {
        // 当 a.score > b.score 时返回 true,
        // 意味着 a (分数高的) 优先级更低 -> 最小堆
        return a.score > b.score;
    }
};

int main() {
    std::priority_queue<Student, std::vector<Student>, CompareStudentMin> student_min_heap;

    student_min_heap.push({"李四", 88});
    student_min_heap.push({"王五", 100});
    student_min_heap.push({"张三", 95});

    std::cout << "\nStudent 最小堆 (自定义 Functor):" << std::endl;
    while (!student_min_heap.empty()) {
        std::cout << student_min_heap.top().name << ": " << student_min_heap.top().score << std::endl;
        student_min_heap.pop();
    }
    // 输出:
    // 李四: 88
    // 张三: 95
    // 王五: 100
}

总结

  1. 头文件#include <queue>

  2. 默认行为std::priority_queue<T> 是一个最大堆top() 是最大值)。

  3. 核心操作push(x) 插入 (O(log n)),pop() 移除顶部 (O(log n)),top() 访问顶部 (O(1))。

  4. 实现最小堆std::priority_queue<T, std::vector<T>, std::greater<T>>

  5. 比较器逻辑Compare(a, b) 返回 true 意味着 a 的优先级低于 b

  6. 自定义结构体

    • 重载 operator< 。

    • 提供自定义比较器 。

<think>我们被要求解释std::priority_queue的使用方法。根据引用[1][2][3],priority_queue是一个容器适配器,它提供常数时间获取最大(或最小)元素,以及对数时间的插入和删除。默认情况下,它使用vector作为底层容器,并使用最大堆(max-heap)结构。 主要特点: 1. 包含头文件:<queue> 2. 默认是最大堆(即队首元素最大) 3. 可以自定义比较函数来改变排序规则(如最小堆) 使用步骤: 1. 创建priority_queue对象 2. 插入元素(push) 3. 访问顶部元素(top) 4. 弹出顶部元素(pop) 5. 其他操作(如判空empty,大小size) 我们将通过几个代码示例来说明: 示例1:最大堆(默认) 示例2:最小堆(使用自定义比较函数) 示例3:自定义数据类型的优先队列 注意:priority_queue没有迭代器,不能遍历。 下面我们分别给出代码示例: </think>### `std::priority_queue` 优先队列使用详解 `std::priority_queue` 是 C++ 标准库中的容器适配器,用于实现优先级队列的数据结构。它基于**堆(heap)** 实现,提供高效的元素访问和操作[^2]。 #### 🧠 核心特性 | 操作 | 时间复杂度 | 说明 | |-----------------|------------|-------------------------------| | `top()` | $O(1)$ | 访问顶部元素(最高优先级) | | `push()` | $O(\log n)$| 插入元素 | | `pop()` | $O(\log n)$| 删除顶部元素 | | `empty()` | $O(1)$ | 检查队列是否为空 | | `size()` | $O(1)$ | 返回队列大小 | #### 📝 基本使用(包含头文件) ```cpp #include <queue> // 必需头文件 #include <vector> // 默认底层容器 #include <iostream> ``` #### ⚙️ 三种典型用法示例 ##### 1️⃣ **默认最大堆**(降序排列) ```cpp std::priority_queue<int> maxHeap; maxHeap.push(3); maxHeap.push(1); maxHeap.push(4); while (!maxHeap.empty()) { std::cout << maxHeap.top() << " "; // 输出:4 3 1 maxHeap.pop(); } ``` > 默认大顶堆,顶部始终是最大元素[^3] ##### 2️⃣ **最小堆**(升序排列) ```cpp // 使用greater比较器创建最小堆 std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap; minHeap.push(3); minHeap.push(1); minHeap.push(4); while (!minHeap.empty()) { std::cout << minHeap.top() << " "; // 输出:1 3 4 minHeap.pop(); } ``` ##### 3️⃣ **自定义数据结构排序** ```cpp struct Task { int priority; std::string name; // 重载<运算符(最大堆) bool operator<(const Task& other) const { return priority < other.priority; } }; std::priority_queue<Task> taskQueue; taskQueue.push({3, "Low"}); taskQueue.push({9, "High"}); taskQueue.push({5, "Mid"}); // 输出:High(9) Mid(5) Low(3) while (!taskQueue.empty()) { auto task = taskQueue.top(); std::cout << task.name << "(" << task.priority << ") "; taskQueue.pop(); } ``` #### 🔧 自定义比较函数(Lambda表达式) ```cpp auto cmp = [](const Task& a, const Task& b) { return a.priority > b.priority; // 最小堆规则 }; std::priority_queue<Task, std::vector<Task>, decltype(cmp)> customQueue(cmp); // 注意:需传入构造函数 customQueue.push({3, "A"}); customQueue.push({1, "B"}); std::cout << customQueue.top().name; // 输出B(优先级1最小) ``` #### 💡 关键注意事项 1. **底层容器选择** 默认使用 `std::vector`,也可指定 `std::deque`: ```cpp std::priority_queue<int, std::deque<int>> dequeBased; ``` 2. **元素访问限制** `priority_queue` **不支持随机访问**,只能通过 `top()` 访问堆顶元素。 3. **性能权衡** - ✅ 优势:快速获取最大/最小元素 - ⚠️ 限制:不适合需要频繁查找中间元素的场景 4. **堆操作原理** 插入/删除时通过上浮(sift up)和下沉(sift down)操作维护堆属性: $$ \begin{cases} \text{插入:} & O(\log n) \text{ 上浮操作} \\ \text{删除:} & O(\log n) \text{ 下沉操作} \end{cases} $$ #### 🌐 应用场景 - 任务调度系统(如后台窗口管理) - 实时数据处理(获取Top K元素) - 图算法(Dijkstra最短路径) - 事件驱动模拟 > 完整文档参考:[C++标准库优先级队列](https://en.cppreference.com/w/cpp/container/priority_queue)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值