【C++】优先级队列

一. 优先级队列简介

        首先,它的本质就是一个(heap)。

在C++中,优先级队列(Priority Queue)是一种特殊的容器适配器,它提供了一个能实现类似堆的结构的功能。优先级队列保证了队首元素总是最大的(或者在最小的元素在优先级最高的定义下是最小的),与普通队列不同,优先级队列的出队操作(front和pop)总是取出具有最高优先级的元素,而入队操作(push)会将新元素插入到正确的位置以保持优先级顺序。这种顺序是通过堆这种数据结构来实现的。

在实践中,优先级队列通常用于需要快速检索最大(或最小)元素的场景,例如在图算法中的Dijkstra算法,或者在处理事件时根据事件的优先级来排序。

二. 使用示例

在C++标准库中,优先级队列(std::priority_queue)是在<queue>头文件中定义的。它有以下几个基本操作:

  • empty():判断优先级队列是否为空。
  • size():返回优先级队列中元素的数量。
  • top():返回队首元素,即优先级最高的元素。
  • push(const T& value):向优先级队列中添加一个元素,并自动根据优先级排序。
  • pop():移除并返回队首元素(优先级最高的元素)。

在使用它时要注意,它默认使用 std::less 作为比较函数,这意味着它默认构建的是最大堆。如果你想创建一个最小堆,你可以在创建优先级队列时提供一个不同的比较函数,比如 std::greater


下面是一个简单的例子,展示了如何在C++中使用优先级队列:

#include <iostream>
#include <queue>

int main() {
    std::priority_queue<int> pq; // 默认创建最大堆

    pq.push(1);
    pq.push(2);
    pq.push(3);
    pq.push(4);
    pq.push(5);
    while (!pq.empty()) {
        std::cout << pq.top() << " "; // 输出:5 4 3 2 1
        pq.pop();
    }

    return 0;
}

创建它时,还可以指定底层元素类型,容器类型(默认为std::vector)以及可选的比较对象。

比如下面的最小堆:

#include <iostream>
#include <queue>

int main() {
    std::priority_queue<int, std::vector<int>, std::greater<int>> pq; // 创建最小堆

    pq.push(5);
    pq.push(4);
    pq.push(3);
    pq.push(2);
    pq.push(1);
    while (!pq.empty()) {
        std::cout << pq.top() << " "; // 输出:1 2 3 4 5
        pq.pop();
    }

    return 0;
}

这样就改变了队列的优先级。

三. 模拟实现 priority_queue

因为优先级队列的底层和  的实现逻辑相同,所以得先实现向上调整算法,和向下调整算法,这里因为要适配优先级队列,所以会稍微有点差别。这里建的是一个大顶堆

1.堆的写法应该是这样:

// 大顶堆
// 向上调整
void adjust_up(size_t child) {
    size_t parent = (child - 1) / 2;
    while (child > 0) {
        if (arr[parent] > arr[child]) {
            swap(c[parent], c[child]);
            child = parent;
            parent = (parent - 1) / 2;
        }
        else
            break;
    }
}

// 向下调整
void adjust_down(size_t parent) {
    size_t child = parent * 2 + 1;
    while (child < c.size()) {
        if (child + 1 < c.size() && arr[child] > arr[child+1])
            child++;
        if (arr[parent] > arr[child]) {
            swap(c[parent], c[child]);
            parent = child;
            child = child * 2 + 1;
        }
        else
            break;
    }
}

而在优先级队列中是这样写的:

template<class T>
struct Greater 
{
    bool operator()(const T& x, const T& y) {
        return x > y;
    }
};
template<class T>
struct Less
{
    bool operator()(const T& x, const T& y) {
        return x < y;
    }
};

template <class T, class Container = vector<T>, class Compare = less<T> >
class priority_queue
{
public:
    void adjust_up(size_t child) {
        size_t parent = (child - 1) / 2;
        while (child > 0) {
            if (comp(c[parent], c[child])) {
                swap(c[parent], c[child]);
                child = parent;
                parent = (parent - 1) / 2;
            }
            else
                break;
        }
    }
    void adjust_down(size_t parent) {
        size_t child = parent * 2 + 1;
        while (child < c.size()) {
            if (child + 1 < c.size() && comp(c[child], c[child + 1]))
                child++;
            if (comp(c[parent], c[child])) {
                swap(c[parent], c[child]);
                parent = child;
                child = child * 2 + 1;
            }
            else
                break;
        }
    }
    // ......
private:
    Container c;
    Compare comp;
};

可以看到这里因为有两个优先级,所以用到了两个重载了 ( ) 操作符的仿函数,使其可以像函数一样被调用,这一点很像C语言中的函数调用。

完整实现代码

#include<vector>
using namespace std;
namespace pq
{
    template<class T>
    struct Greater 
    {
        bool operator()(const T& x, const T& y) {
            return x > y;
        }
    };
    template<class T>
    struct Less
    {
        bool operator()(const T& x, const T& y) {
            return x < y;
        }
    };
    // 默认建大顶堆
    template <class T, class Container = vector<T>, class Compare = less<T> >
    class priority_queue
    {
    public:
        priority_queue(){}
        void adjust_up(size_t child) {
            size_t parent = (child - 1) / 2;
            while (child > 0) {
                if (comp(c[parent], c[child])) {
                    swap(c[parent], c[child]);
                    child = parent;
                    parent = (parent - 1) / 2;
                }
                else
                    break;
            }
        }
        void adjust_down(size_t parent) {
            size_t child = parent * 2 + 1;
            while (child < c.size()) {
                if (child + 1 < c.size() && comp(c[child], c[child + 1]))
                    child++;
                if (comp(c[parent], c[child])) {
                    swap(c[parent], c[child]);
                    parent = child;
                    child = child * 2 + 1;
                }
                else
                    break;
            }
        }
        bool empty() const {
            return c.empty();
        }
        size_t size() const {
            return c.size();
        }
        T& top() const {
            if (empty())
                return;
            return c[0];
        }
        void push(const T& x) {
            c.push_back(x);
            adjust_up(c.size() - 1);
        }
        void pop() {
            if (empty())
                return;
            swap(c[0], c[c.size() - 1]);
            c.pop_back();
            adjust_down(0);
        }
    private:
        Container c;
        Compare comp;
    };
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值