【C++ STL】priority_queue


priority_queue

1 接口使用

优先级队列priority_queue同样是个容器适配器,不同于stack和queue只是对容器的简单封装。它有三个模板参数:

template <class T, /* 数据类型 */
	      class Container = vector<T>, /* 适配容器 */
		  class Compare = less<typename Container::value_type> > /* 仿函数 */
class priority_queue;

priority_queue要求底层容器必须具有随机访问迭代器,支持empty,size,front,push_back,pop_back几种接口,一般使用vector作底层容器。

priority_queue就是堆,能够实现堆的各种算法。因为要维护容器本身的特性,所以不支持遍历。

class Compare = less<typename Container::value_type>

priority_queue默认数值大优先级高,也就是默认大堆。想要排成小堆,需要指定priority_queue的仿函数参数,传入greator<T>

接口声明解释
priority_queue (Compare& cmp = Compare(), Container& ctnr = Container())构造函数
bool empty() const判空
size_type size() const元素个数
value_type& top()栈顶元素
void push (const value_type& val)尾插
void pop()尾删

Top-K

class Solution {
public:
    //建大堆会选出整个数组的最大值,减小堆才能选出第k大的数
    int findKthLargest(vector<int>& nums, int k) {
        vector<int>::iterator pos = nums.begin() + k;
        //k个数的小堆
        priority_queue<int, vector<int>, greater<int>> pq(nums.begin(), nums.begin() + k);
        while (pos != nums.end()) {
            if (*pos > pq.top()) {
                pq.pop();
                pq.push(*pos);
            }
            ++pos;
        }
        return pq.top();
    }
};

2 模拟实现

基本接口

template<class T, class Container = std::vector<T>, class Compare = less<T>>
class priority_queue
{
public:
    void push(const T& x) {
        _con.push_back(x);
        adjust_up(_con.size() - 1); //向上调整
    }
    void pop() {
        swap(_con[0], _con[size() - 1]);
        _con.pop_back();
        adjust_down(0); //向下调整
    }
    bool empty() const {
        return _con.empty();
    }
    int size() const {
        return _con.size();
    }
    T& top() const {
        return _con.front();
    }
private:
    Container _con;
    Compare _cmp;
};

向上调整算法

void adjust_up(int child)
{
    
    int parent = (child - 1) / 2;

    while (child > 0)
    {
        //比较父节点与子节点的大小,并进行交换
        if (_cmp(_con[parent], _con[child]))
            swap(_con[parent], _con[child]);
        else
            break;

        child = parent;
        parent = (child - 1) / 2;
    }
}

向下调整算法

void adjust_down(int parent)
{
    int child = parent * 2 + 1;

    while (child < _con.size())
    {	
        //找出子节点的大一个
        if (child + 1 < _con.size() && _cmp(_con[child], _con[child + 1]))
            child++;
		
        //比较父节点与子节点的大小,并进行交换
        if (_cmp(_con[parent], _con[child]))
            swap(_con[parent], _con[child]);
        else
            break;

        parent = child;
        child  = parent * 2 + 1;
    }
}

堆插入就是数组尾插一个元素,然后向上调整。

此时堆的性质可能被破坏,不过只会影响该结点到根结点所在路径上的所有结点,故顺势向上调整:一直交换结点数值直到满足堆的性质即可。

在这里插入图片描述

堆删除就是将尾元素覆盖到堆顶,然后向下调整。

只是堆顶元素不满足性质,其左右子树还是原样。只需将堆顶元素逐步向下调整:将根结点与其较大/小的子结点交换,只要父结点比子结点中任意一个大/小,就进行交换,直到交换到叶结点或不满足条件为止。

在这里插入图片描述

仿函数

决定大堆还是小堆,在于向上/向下调整算法中的父子节点的比较关系:

if (_cmp(_con[parent], _con[child])) {
	swap(_con[parent], _con[child]);
}

if (child + 1 < _con.size() && _cmp(_con[child], _con[child + 1])) {
    child++;
}

比较大小操作符写死不便用户修改,使用宏定义,函数指针都比较复杂,还有一种简单的方式就是仿函数。

仿函数又名函数对象,本质是对象,通过重载()操作符模仿函数的调用方式。

仿函数相当于更高级的泛型,使用仿函数能够改变执行逻辑,仿函数内部的实现完全由用户自定,拥有极大的自定义空间。

template <class T>
struct less {
    bool operator()(const T& left, const T& right) const {
        return left < right;
    }
};
template <class T>
struct greater {
    bool operator()(const T& left, const T& right) const {
        return left > right;
    }
};

Less less;
less(1, 2);
Greater greater;
greater(1, 2);

仿函数本质是一种类型,所以可以作模版参数,让用户定义类的时候指定。

template <class T, class Container = vector<T>, class Compare = Less<T>> 
class priority_queue {
    void adjust_up(int child) {
        if (_cmp(_con[parent], _con[child]))
            //...
    }
    void adjust_down(int parent) {
        if (child + 1 < _con.size() && _cmp(_con[child], _con[child + 1]))
            //...
        if (_cmp(_con[parent], _con[child]))
            //...
    }
    Compare _cmp;
};

在这里插入图片描述

#include <iostream>
#include <vector>

namespace test
{
template<class T>
struct less
{
    bool operator()(const T& x, const T& y)
    {
        return x < y;
    }
};

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

template<class T, class Container = std::vector<T>, class Compare = less<T>>
class priority_queue
{
public:
    void adjust_up(int child)
    {
        int parent = (child - 1) / 2;

        while (child > 0)
        {
            if (_cmp(_con[parent], _con[child]))
                swap(_con[parent], _con[child]);
            else
                break;

            child = parent;
            parent = (child - 1) / 2;
        }
    }

    void adjust_down(int parent)
    {
        int child = parent * 2 + 1;

        while (child < _con.size())
        {
            if (child + 1 < _con.size() && _cmp(_con[child], _con[child + 1]))
                child++;

            if (_cmp(_con[parent], _con[child]))
                swap(_con[parent], _con[child]);
            else
                break;

            parent = child;
            child  = parent * 2 + 1;
        }
    }

    void push(const T& x)
    {
        _con.push_back(x);
        adjust_up(_con.size() - 1);
    }

    void pop()
    {
        _con[0] = _con[_con.size() - 1];
        _con.pop_back();
        adjust_down(0);
    }

    size_t size()
    {
        return _con.size();
    }

    bool empty()
    {
        return _con.empty();
    }

    T& top()
    {
        return _con[0];
    }

private:
    Container _con;
    Compare _cmp;
};

}
  • 10
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SuhyOvO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值