- empty():检测容器是否为空
- size():返回容器中有效元素个数
- front():返回容器中第一个元素的引用
- push_back():在容器尾部插入元素
- pop_back():删除容器尾部元素
优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆。
函数声明 | 接口说明 |
---|---|
priority_queue()/priority_queue(first,last) | 构造一个空的优先级队列 |
empty( ) | 检测优先级队列是否为空,是返回true,否则返回false |
top( ) | 返回优先级队列中最大(最小元素),即堆顶元素 |
push(x) | 在优先级队列中插入元素x |
pop() | 删除优先级队列中最大(最小)元素,即堆顶元素 |
【注意】
数组中的第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 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();
}
};
通过对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语言)数据结构二叉树之堆