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< 。

    • 提供自定义比较器 。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值