C++——优先级队列

1.priority_queue的介绍和使用

1.1 priority_queue的介绍

  1. 优先级队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
  3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
  4. 优先级队列被实现为容器适配器,容器适配器是使用特定容器类的封装对象作为其底层容器的类,提供一组特定的成员函数来访问其元素。元素只能从特定容器的顶部弹出。
  5. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
  • empty():检测容器是否为空
  • size():返回容器中有效元素个数
  • front():返回容器中第一个元素的引用
  • push_back():在容器尾部插入元素
  • pop_back():删除容器尾部元素
  1. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector
  2. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。

1.2 priority_queue的使用

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

函数声明接口说明
priority_queue()/priority_queue(first,last)构造一个空的优先级队列
empty( )检测优先级队列是否为空,是返回true,否则返回false
top( )返回优先级队列中最大(最小元素),即堆顶元素
push(x)在优先级队列中插入元素x
pop()删除优先级队列中最大(最小)元素,即堆顶元素

【注意】

  1. 默认情况下,priority_queue是大堆。
  2. 如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供> 或者< 的重载

1.3 在OJ中的使用

数组中的第K个最大元素
思路:
1、建大堆:把数组中的n个数全部放到默认的优先级队列pq里,此时pq就是一个n个数的大堆,然后让优先级队列pop K次,此时堆顶的元素就是数组中的第K个最大元素。
2、建小堆:先把数组的前K个数push到优先级队列pq里(这里比较容易出错),然后从K+1个数开始遍历数组,如果遇到比pq的堆顶元素大的数,那么就先把堆顶pop掉然后把这个数push到pq里,这样遍历到最后一个数,此时pq中的K个元素就是前K大的K个数,堆顶就是数组中的第K个最大元素。
代码:

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        // //用默认的去 建大堆,然后pop  k次
        // priority_queue<int> pq (nums.begin(),nums.end());
        // //找第k个最大的数pop  k-1次。
        // while(--k)
        // {
        //     pq.pop();
        // }

        // return pq.top();


        //建小堆
        priority_queue<int,vector<int>,greater<int>> pq 
        (nums.begin(),nums.begin()+k);
        //容易出错的地方nums.begin()到nums.begin()+k,才是k个元素
        
        for(int i=k;i<nums.size();i++)
        {
            if(nums[i]>pq.top())
            {
                pq.pop();
                pq.push(nums[i]);
            }

        }

        return pq.top();
    }
};

1.4 priority_queue的模拟实现

通过对priority_queue的底层结构就是堆,因此此处只需对vector或者deque这样的容器进行通用的封装,把他们按照堆的模式封装即可。

仿函数/函数对象

仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),也就是对于()的运算符重载,这个类就有了类似函数的行为,就是一个仿函数类了。目的是为了代替C语言中的函数指针
运用在排序中用户可以自己定义升序或者降序。
在这里插入图片描述
对应到堆里面就是大堆或是小堆。
在这里插入图片描述

标准库仿函数的模拟实现:

//仿函数/函数对象
template<class T>
class less
{
public:
	bool operator()(const T& x, const T& y) const
	{
		return x < y;
	}
};

template<class T>
class greater
{
public:
	bool operator()(const T& x, const T& y) const
	{
		return x > y;
	}
};
  • 对于内置类型,天然的就支持<和>这样比大小的操作。
  • 对于自定义类型,如果类里重载了<和>运算符,支持类与类之间的<和>操作,并且功能符合预期,那下面代码中的<和>就转化为对应的运算符重载进行比较即可;若功能不满足我们的预期,那么需要自己去实现对应的仿函数。

功能不满足预期的举例:

//日期类
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}

	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}

	friend ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}

private:
	int _year;
	int _month;
	int _day;
};


struct PDateLess//需要自己写仿函数,这里是自己实现的仿函数
{
	bool operator()(const Date* d1, const Date* d2)
	{
		return *d1 < *d2;
	}
};

struct PDateGreater//需要自己写仿函数,这里是自己实现的仿函数
{
	bool operator()(const Date* d1, const Date* d2)
	{
		return *d1 > *d2;
	}
};


void TestPriorityQueue()
{
	// 大堆
	priority_queue<Date*, vector<Date*>, PDateLess> q3;
	//定义类的时候用的是PDateLess——仿函数的类型
	q3.push(new Date(2018, 10, 29));
	q3.push(new Date(2018, 10, 28));
	q3.push(new Date(2018, 10, 30));
	cout << *q3.top() << endl;

	// 小堆
	priority_queue<Date*, vector<Date*>, PDateGreater> q4;
	//定义类的时候用的是PDateGreater——仿函数的类型
	q4.push(new Date(2018, 10, 29));
	q4.push(new Date(2018, 10, 28));
	q4.push(new Date(2018, 10, 30));
	cout << *q4.top() << endl;
}

int main()
{
	TestPriorityQueue();
	return 0;
}

仿函数的使用:不管是用库里面的还是自己实现的仿函数,在定义一个类的时候,用的是仿函数的类型(一般类名就是类型,但是在模板类中需要加上模板参数才是一个完整的类型),在类里会拿这个类型去实例化一个对象,通过这个对象来调用对于()的运算符重载,以此来达到控制类里对应的行为。

下面是类里向上调整和向下调整的代码:

向上调整

void adjust_up(size_t child)
{
	Compare com;//实例化一个对象com
	size_t parent = (child - 1) / 2;
	while (child > 0)
	{
		//if (_con[parent] < con[child])
		if (com(_con[parent], _con[child]))//通过这个对象com来调用对于()的运算符重载
		{
			swap(_con[child], _con[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

向下调整

void adjust_down(size_t parent)
{
	Compare com;//实例化一个对象com
	size_t child = parent * 2 + 1;
	while (child < _con.size())
	{
		//if (child+1 < _con.size() && _con[child] < _con[child+1])
		if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))//通过对象com来调用对于()的运算符重载
		{
			child++;
		}

		//if (_con[parent] < _con[child])
		if (com(_con[parent], _con[child]))
		{
			swap(_con[child], _con[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

如果对堆其他知识有不清楚的老铁可以看这篇文章😊
(C语言)数据结构二叉树之堆

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
优先队列(priority_queue)是C++中的一种数据结构,它可以按照元素的优先级进行插入和删除操作。在C++中,优先队列默认使用vector作为底层存储数据的容器,并使用堆算法将vector中的元素构造成堆的结构。优先队列最常用的操作包括push、pop和top。 要遍历优先队列中的元素,可以使用while循环和empty函数来实现。首先,可以使用top函数获取优先队列中的最大(或最小)元素,并将其打印出来。然后,使用pop函数将该元素从优先队列中删除。重复这个过程,直到优先队列为空,即使用empty函数判断优先队列是否为空。在每次循环中,可以将获取的元素打印出来。 下面是一个示例代码来遍历优先队列: ```c++ #include <iostream> #include <queue> using namespace std; int main() { priority_queue<int> pq; pq.push(3); pq.push(8); pq.push(2); pq.push(6); pq.push(9); while (!pq.empty()) { cout << pq.top() << " "; pq.pop(); } cout << endl; return 0; } ``` 以上代码创建了一个优先队列pq,并依次插入了5个元素。然后使用while循环和empty函数遍历优先队列,每次输出堆顶元素,并将其从优先队列中删除,直到优先队列为空。最终输出的结果是按照从大到小的顺序输出了优先队列中的所有元素。 因此,c++优先队列的遍历可以通过循环的方式来实现。在每次循环中,使用top函数获取堆顶元素并打印,然后使用pop函数将其删除。重复这个过程直到优先队列为空。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++算法(优先队列、遍历算法、查找算法、排序算法)](https://blog.csdn.net/qq_41915323/article/details/94664541)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [C++——优先级队列](https://blog.csdn.net/qq_55712347/article/details/128874870)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

有效的放假者

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

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

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

打赏作者

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

抵扣说明:

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

余额充值