1.认识优先级队列
如果我们给每个元素都分配一个数字来标记其优先级,不妨设较小的数字具有较高的优先级,这样我们就可以在一个集合中访问优先级最高的元素并对其进行查找和删除操作了。
优先级队列(priority queue) 是0个或多个元素的集合,每个元素都有一个优先权,对优先级队列执行的操作有(1)查找(2)插入一个新元素 (3)删除
一般情况下,查找操作用来搜索优先权最大的元素,删除操作用来删除该元素 。对于优先权相同的元素,可按先进先出次序处理或按任意优先权进行
在默认情况下优先级最高的通常是队列中最大的数字,当然我们也可以只用仿函数来更改其队列中元素的优先级,优先级队列的底层实质上就是堆,也就是数组
2.模拟实现优先级队列
首先回顾一下完全二叉树中父节点与左右子结点的关系:left child = parent * 2 + 1; right child = parent * 2 + 2; parent = (child - 1) / 2
namespace bit
{
template<class T,class Container = vector<T>>
class priority_queue
{
public:
//向上调整
void AdjustUp(size_t child)
{
size_t parent = (child - 1) / 2;
while (child > 0)
{
//将大的值移向堆顶,建大堆
if (_con[parent] < _con[child])
{
swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
//向下调整
void AdjustDown(size_t parent)
{
//假设为左子结点
size_t child = parent * 2 + 1;
while (child > 0)
{
//找到大的子结点
if (child + 1 < _con.size() && _con[child] < _con[child + 1])
{
child++;
}
if (_con[child] > _con[parent])
{
swap(_con[child], _con[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void push(const T& x)
{
_con.push_back(x);
//从堆底向上调整
AdjustUp(_con.size() - 1);
}
void pop()
{
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
//从堆顶向下调整
AdjustDown(0);
}
const T& top()
{
return _con[0];
}
size_t size()
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
private:
Container _con;
};
}
3.仿函数
首先我们要知道仿函数的本质是一个类,为了使代码更加具有灵活性,我们就使用到了仿函数来重载 operator(),以达到改变大/小堆的功能
template<class T>
class Less
{
public:
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T>
class Greater
{
public:
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
namespace bit
{
//默认是大堆
template<class T,class Container = vector<T>,class Compare = Less<T>>
class priority_queue
{
public:
//向上调整
void AdjustUp(size_t child)
{
Compare com;
size_t parent = (child - 1) / 2;
while (child > 0)
{
//将大的值移向堆顶,建大堆
//if (_con[child] < _con[parent])
if (com(_con[child],_con[parent]))
{
swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
//向下调整
void AdjustDown(size_t parent)
{
Compare com;
//假设为左子结点
size_t child = parent * 2 + 1;
while (child > 0)
{
//找到大的子结点
//if (child + 1 < _con.size() && _con[child] < _con[child + 1])
if (child + 1 < _con.size() && com(_con[child],_con[child + 1]))
{
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;
}
}
}
void push(const T& x)
{
_con.push_back(x);
//从堆底向上调整
AdjustUp(_con.size() - 1);
}
void pop()
{
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
//从堆顶向下调整
AdjustDown(0);
}
const T& top()
{
return _con[0];
}
size_t size()
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
private:
Container _con;
};
}
4.实战代码练习
LCR076.数组中的第K个最大元素,本题使用优先级队列可以很简单的解决问题
class Solution {
public:
int findKthLargest(vector<int>& nums, int k)
{
//将元素存储到优先级队列
priority_queue<int> pq;
for(auto& e : nums)
{
pq.push(e);
}
//取出前k-1个最大元素,剩下的元素中最大的元素就是第k个最大元素
for(int i = 0;i < k - 1;i++)
{
pq.pop();
}
return pq.top();
}
};