c++中优先级队列

1. priority_queue的介绍和使用

1.1 priority_queue的介绍

priority_queue(优先队列)是 C++ 标准模板库(STL)中的一个容器适配器,它提供了一种按照优先级自动排序的队列数据结构。与普通队列的 “先进先出(FIFO)” 不同,优先队列中元素的出队顺序取决于其优先级,优先级最高的元素总是最先被弹出。
核心特性:
自动排序:插入元素时会自动按照预设的优先级规则排序(默认是降序,即最大元素优先)。
访问限制:只能访问优先级最高的元素(队首),无法直接访问其他位置的元素。
基于底层容器:默认使用 vector 作为底层容器,也可指定为 deque 等序列容器。
排序准则:默认使用 less(大顶堆,最大值优先), greater(小顶堆,最小值优先)也可自定义比较器实现小顶堆或其他排序规则。
在这里插入图片描述

1.2 priority_queue的使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成
堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:
默认情况下priority_queue是大堆

头文件<queue>

函数声明接口说明
priority_queue()构造一个空的优先级队列
priority_queue(first, last)以一个迭代器区间构造优先级队列
empty( )检测优先级队列是否为空,是返回true,否则返回false
top( )返回优先级队列中最大(最小元素),即堆顶元素
push(x)在优先级队列中插入元素x
pop()删除优先级队列中最大(最小)元素,即堆顶元素
vector<int> v{ 1,3,9,5,8,4,6 };
priority_queue<int> pq();
priority_queue<int> pq1(v.begin(),v.end());
while (!pq1.empty())
{
    cout << pq1.top();
    pq1.pop();
}

在这里插入图片描述

2. 仿函数

2.1 仿函数的定义

在 C++ 中,仿函数(Functor) 是一种行为类似函数的对象,它通过重载 operator() 运算符,使得对象可以像函数一样被调用。这种特性让仿函数兼具函数的灵活性和对象的状态存储能力,在 STL(标准模板库)中被广泛用于算法的参数(如排序、查找的自定义规则)。
仿函数的基本定义
仿函数通常是一个类(或结构体),其中重载了 operator(),语法如下:

tempalte<class T>
class less
{
	public:
		bool operator()(T a,T b)
			{
				return a<b;
			} 
}

2.2 仿函数的使用

  1. 使用时,先创建类的对象,再像调用函数一样使用 对象名(参数) 触发 operator() 的执行。
less<int> ls;
std::cout<<ls(3,4);

在这里插入图片描述
3小于4,返回真。

  1. 也可以通过匿名对象的形式调用仿函数:
std::cout<<less<int>()(2,4);

在这里插入图片描述
先用less()创建一个匿名对象,然后再利用()符号重载调用仿函数。

3. 利用仿函数模拟priority_queue

1. class priority_queue的成员类型:

template <class T, class Container = vector<T>, class Compare = less<T>>
class priority_queue
{
public:
   
private:
    Container c;//使用适配器,默认为vector。
    Compare comp;//比较类,里面有比较的函数。
};

注意:compare为less时,优先队列为大堆,compare为greater时,优先队列为小堆。
Compare comp;比较类,里面有比较的仿函数。默认为less,less内有对符号()重载,大概为:

template <class T>
class less
{
	bool operator()(const T&a,const T&b)
	{
		return a<b;
	}
}

2. class priority_queue的拷贝构造:

	priority_queue()
    {
    };
    template <class InputIterator>
    priority_queue(InputIterator first, InputIterator last)
    {
        while (first != last)
        {
            c.push_back(*(first++));
            adjust_up();
        }
    }

priority_queue可以使用迭代器区间,构造优先队列。其原理是对底层的vector或deque进行尾插,每插入一个元素,进行从插入位置向上调整,这里我们默认的是less,所以是一个大堆,把大的值向上调整。
在这里插入图片描述
向上调整代码:

 void adjust_up()
    {
        int child = c.size() - 1;
        int parent = (child - 1) / 2;
        while (comp(c[parent], c[child]) && parent >= 0)
        {
            swap(c[parent], c[child]);
            child = parent;
            parent = (child - 1) / 2;
        }
    }

注意:我们默认的比较仿函数是less中的operator(),也就是比较小的话为真,这是堆为大堆,也就是堆顶为最大元素。comp(c[parent], c[child]),也就是parent小于child时,交换两个位置的元素,这样就可以让child节点,也就是向上调整时,下面的大于上面的元素上移。
3. 插入函数

void push(const T& x)
    {   
        c.push_back(x);
        adjust_up();
    };

4. 删除函数

	void pop()
	    {
	        swap(c[0], c[c.size() - 1]);
	        c.pop_back();
	        adjust_down();
	    };

在这里插入图片描述
向下调整代码:

void adjust_down()
	{
	    int parent = 0;
	    int child = parent * 2 + 1;
	    while (child<c.size())
	    {
	        if (comp(c[child], c[child + 1]) && child + 1 < c.size())
	            child += 1;
	        if (comp(c[parent], c[child]))
	        {
	            swap(c[parent], c[child]);
	            parent = child;
	            child= parent * 2 + 1;
	        }
	        else
	        {
	            break;
	        }
	    }
	}

注意:我们默认的比较仿函数是less中的operator(),也就是比较小的话为真,这是堆为大堆,也就是堆顶为最大元素。comp(c[parent], c[child]),也就是parent小于child时,交换两个位置的元素,这样就可以使上面小的元素和小面大的元素交换位置。
5. 整体代码:

template <class T, class Container = vector<T>, class Compare = less<T> >
class priority_queue
{
public:
    priority_queue()
    {
    };
    template <class InputIterator>//利用迭代器区间构造堆
    priority_queue(InputIterator first, InputIterator last)
    {
        while (first != last)
        {
            c.push_back(*(first++));
            adjust_up();
        }
    }
    void adjust_up()//向上调整堆
    {
        int child = c.size() - 1;
        int parent = (child - 1) / 2;
        while (comp(c[parent], c[child]) && parent >= 0)
        {
            swap(c[parent], c[child]);
            child = parent;
            parent = (child - 1) / 2;
        }
    }
	    void adjust_down()//向下调整堆
	{
	    int parent = 0;
	    int child = parent * 2 + 1;
	    while (child<c.size())
	    {
	        if (comp(c[child], c[child + 1]) && child + 1 < c.size())
	            child += 1;
	        if (comp(c[parent], c[child]))
	        {
	            swap(c[parent], c[child]);
	            parent = child;
	            child= parent * 2 + 1;
	        }
	        else
	        {
	            break;
	        }
	    }
	}
    bool empty() const//堆是否为空
    {
        return c.empty();
    };

    size_t size() const//堆中元素多少
    {
        return c.size();
    };

    const T& top() const//访问堆顶元素
    {
        return c.front();
    };
    
    void push(const T& x)//插入元素
    {   
        c.push_back(x);
        adjust_up();
    };

    void pop()//删除堆顶
    {
        swap(c[0], c[c.size() - 1]);
            c.pop_back();
        adjust_down();
    };

private:

    Container c;
    Compare comp;

};

4. 如何创建小堆优先级队列

我们想创建小堆,可以再创建优先级队列时,在模板实例化时,传greater:

priority_queue<int,vector<int>,greater<int>> p();

因为greater和less比较逻辑是相反的,所以创建出来的是一个小堆。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

aaa最北边

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

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

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

打赏作者

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

抵扣说明:

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

余额充值