之前已经提到了队列(queue),队列是一种先进先出(First in First out,FIFO)的数据类型。每次元素的入队都只能添加到队列尾部,出队时从队列头部开始出。
优先级队列(priority_queue)其实,不满足先进先出的条件,更像是数据类型中的“堆”。优先级队列每次出队的元素是队列中优先级最高的那个元素,而不是队首的元素。这个优先级可以通过元素的大小等进行定义。 比如定义元素越大优先级越高,那么每次出队,都是将当前队列中最大的那个元素出队。个人感觉这就是所谓“优先级”的定义。
现在看优先级队列是不是就是“堆”了,如果最大的元素优先级最高,那么每次出队的就是当前队列中最大的元素,那么队列实际就相当于一个大根堆,每次将堆根节点元素弹出,重新维护大根堆,就可以实现一个优先级队列。
优先级队列是一个类模板,Compare是比较的方式,默认是大根堆(就是元素值越大,优先级越高);如果使用C++基本数据类型,可以直接使用自带的less和greater这两个仿函数(默认使用的是less,就是构造大根堆)。使用自定义的数据类型的时候,可以重写比较函数,也可以进行运算符重载(less重载小于“<”运算符,构造大根堆;greater重载大于“>”运算符,构造小根堆)。
priority_queue<int, vector<int>, less<int>> priQueMaxFirst
priQueMaxFirst.push(3);
priQueMaxFirst.push(2);
priQueMaxFirst.push(6);
priQueMaxFirst.push(4);
priQueMaxFirst.push(8);
priQueMaxFirst.push(9);
priQueMaxFirst.push(0);
while (!priQueMaxFirst.empty())
{
cout << priQueMaxFirst.top() << " ";
priQueMaxFirst.pop();
}
cout << endl;
1.首先确定模拟的内容,构建好代码初期框架
namespace zxy
{
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);
bool empty();
size_t size();
T& top();
void push(const T& x);
void pop();
private:
Container _con;
Compare cmp;
};
};
2.实现接口
push()
思路:首先我们需要往容器中插入数据,优先级队列既然是一个堆,那么就需要进行堆的调整,我们使用向上调整算法建堆
//向上调整算法
void AdJustUp(size_t child)
{
size_t parent = (child - 1) / 2;
while (child > 0)
{
if (cmp(_con[parent], _con[child]))
{
swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void push(const T& x)
{
//尾插数据
_con.push_back(x);
//向上调整建堆
AdJustUp(_con.size()-1);
}
pop()
思路:这里我们要想清楚pop()和top()的操作区别,pop()是删除堆顶的元素,top()是返回堆顶数据。那么pop()就需要进行进行调整操作,因为我得到堆顶数据之后,堆顶的数据对我而言就没用了,那么就需要删除堆顶数据。堆里面的删除数据不是简单的头删,如果是头删,那么对于顺序容器来说就需要把后面的数据挪动到前面,这样时间消耗太高。所以,在堆里面的删除数据是将堆顶数据与堆尾数据进行交换,然后删除堆尾的数据(此时堆尾的数据就是原来堆顶的数据),从堆顶开始重新向下调整堆
void AdJustDown(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]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
void pop()
{
//heap.pop()删除的是堆顶的数据,底层实现对应的就是首尾交换-》尾删数据-》向下调整
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
AdJustDown(0);
}
仿函数
仿函数实际上是一个类,类里面只是重载了()运算符,在对象调用的时候看起来像函数调用
template <class T>
struct Less
{
//重载()运算符 -》仿函数
bool operator()(const T& x, const T& y) // < 大堆
{
return x < y;
}
};
template <class T>
struct Geater
{
//重载()运算符 -》仿函数
bool operator()(const T& x, const T& y) // > 小堆
{
return x > y;
}
};
#pragma once
#include
#include
#include
#include
#include
using namespace std;
template <class T>
struct Less
{
//重载()运算符 -》仿函数
bool operator()(const T& x, const T& y) // < 大堆
{
return x < y;
}
};
template <class T>
struct Geater
{
//重载()运算符 -》仿函数
bool operator()(const T& x, const T& y) // > 小堆
{
return x > y;
}
};
namespace zxy
{
template <class T, class Container = vector<T>, class Compare = Less<T>>
class priority_queue //默认是 大堆 -》 Less -》 <
{
public:
priority_queue()
{
;
}
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{
_con(first, last);
for (int i = (_con.size() - 2) / 2; i >= 0; i--)
{
AdJustDown(i);
}
}
bool empty()
{
return _con.empty();
}
size_t size()
{
return _con.size();
}
T& top()
{
return _con[0];
}
//向上调整算法
void AdJustUp(size_t child)
{
size_t parent = (child - 1) / 2;
while (child > 0)
{
if (cmp(_con[parent], _con[child]))
{
swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void push(const T& x)
{
//尾插数据
_con.push_back(x);
//向上调整建堆
AdJustUp(_con.size()-1);
}
void AdJustDown(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]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
void pop()
{
//heap.pop()删除的是堆顶的数据,底层实现对应的就是首尾交换-》尾删数据-》向下调整
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
AdJustDown(0);
}
private:
Container _con;
Compare cmp;
};
};
#include "priority_queue .h"
int main()
{
//zxy::priority_queue, Geater> pq;
zxy::priority_queue<int> pq;
pq.push(3);
pq.push(2);
pq.push(6);
pq.push(4);
pq.push(8);
pq.push(9);
pq.push(0);
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}
cout << endl;
return 0;
}