前言
priority_queue,翻译过来就是优先级队列,但是它其实是我们的堆结构(如果堆一些遗忘的可以看一下前面的文章复习一下【数据结构】二叉树——顺序结构——堆及其实现_二叉树顺序结构-CSDN博客),本篇文章就来使用并且模拟实现一下priority_queue。
priority_queue
的使用
这个容器接口就这些,使用起来比较简单:这里就简单使用一下。
int main()
{priority_queue<int> pq1;//无参构造int arr[] = { 1,5,8,9,2,10,6 };//使用一段迭代器区间构造(这里可以使用数组,因为原始指针可以像迭代器那样使用)priority_queue<int> pq2(arr, arr + sizeof(arr) / sizeof(arr[0]));//依次输出pq2while (!pq2.empty()){cout << pq2.top() << " ";pq2.pop();}cout << endl;//往pq1插入数据pq1.push(1);pq1.push(5);pq1.push(7);pq1.push(8);pq1.push(9);//依次输出pq1while (!pq1.empty()){cout << pq1.top() << " ";pq1.pop();}cout << endl;return 0;
}
输出结果如下:
10 9 8 6 5 2 1
9 8 7 5 1
经过观察,我们会发现,默认构建的都是大堆,先看一下容器priority_queue 的定义
这里有三个模版参数,第一个是数据类型,第二个是容器类型(默认是vector),第三个是compare 默认是 less
(这个是一个仿函数,再模拟实现后面再讲解)。
我们不难发现,只有第三个模版参数才也可能控制是大堆还是小堆,(这里,我们如果要建小堆传**greater
**即可)。
//建小堆
int main()
{priority_queue<int, vector<int>, greater<int>> pq1;pq1.push(9);pq1.push(7);pq1.push(5);pq1.push(3);pq1.push(2);while (!pq1.empty()){cout << pq1.top() << " ";pq1.pop();}cout << endl;return 0;
}
2 3 5 7 9
这样就建了小堆;对于仿函数,在模拟实现之后,我相信就会有了深刻的理解。
priority_queue
模拟实现
通过看priority_queue的定义,我们不难发现其也是一个容器适配器,默认容器是vector;所以它的成员变量就是一个容器,其的每一个操作就是在容器中进行一系列操作。
先来看一下priotity_queue
的大致内容:
namespace HL
{//默认——大堆template<class T, class Contianer = vector<T>, class Compare = less<T>>class priority_queue{//向下调整算法void AdjustDown(int parent){}//向上调整算法void AjustUp(int child){}public://默认构造priority_queue() {}//迭代器区间构造template<class InputIterator>priority_queue(InputIterator first, InputIterator last){}void push(const T& x) {}void pop() {}T& top() {}const T& top()const {}size_t size()const {}bool empty()const {}private:Contianer _con;};
}
1、向上调整算法
所谓向上调整算法,就是向上调整当前节点,使其满足堆结构。
主要应用:插入节点时,最后一个节点(插入的节点)向上调整。
//向上调整算法void AjustUp(int child){int parent = (child - 1) / 2;while (parent >= 0){if (_con[parent] < _con[child]){std::swap(_con[parent], _con[child]);}else{break;}child = parent;parent = (child - 1) / 2;}}
2、向下调整算法
所谓向上调整算法,就是向下调整当前节点,使其满足堆结构。
主要应用:
- 构造堆结构时,从最后一个节点的父节点开始依次向下调整,创建堆结构。
- 删除节点时,第一个(堆顶)与最后一个(堆底)交换,然后让第一个(堆顶)节点向下调整。
void AdjustDown(int parent){int child = 2 * parent + 1;while (child < _con.size()){if (child < _con.size() - 1 && _con[child] < _con[child + 1]){child++;}if (_con[parent] < _con[child]){std::swap(_con[parent], _con[child]);}else{break;}parent = child;child = 2 * parent + 1;}}
3、构造函数
构造函数有两种,一是默认构造函数,另一个就是使用迭代器区间构造。
1.默认构造
//默认构造
priority_queue() {}
2.迭代器区间构造
迭代器区间,这里就先将数据插入到容器(vector
)中,再从最后一个节点的父节点开始,依次往下调整建堆即可。
//迭代器区间构造
template<class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{while (first != last){_con.push_back(*first);++first;}for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--){AdjustDown(i);}
}
4、push、pop、top
push
: 插入数据,然后向上调整,保持堆结构。
void push(const T& x)
{_con.push_back(x);AdjustUp(_con.size() - 1);
}
pop
: 删除数据(删除堆顶数据),堆顶数据与堆低数据交换,然后从堆顶位置向下调整即可。
void pop()
{std::swap(_con[0], _con[_con.size() - 1]);AdjustDown(0);
}
top
: 返回堆顶数据
T& top()
{return _con[0];
}
const T& top()const
{return _con[0];
}
5、size、empty、swap
size
: 返回堆中的数据个数
size_t size()const
{return _con.size();
}
empty
: 判断堆是否为空
bool empty()const
{return _con.empty();
}
swap
: 交换函数
void swap(priority_queue& pq)
{std::swap(_con, pq._con);
}
到这里,priority_queue
大致就实现成功了一半,因为这里我们只实现了大堆。
接下来,我们来看一下仿函数,如何实现大小堆。
仿函数
仿函数,也是STL中比较中要的内容;
那为什么叫做仿函数呢?因为,他实际上是一个类,这个类重载了operator() 这样,我们就可以像函数应用实使用这个类。
先来看一下仿函数**less
**:
//这里可以使用struct(默认成员是共有)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;}};
这里实现了仿函数**Less
** 和**Greater
**,我们可以像函数那样去使用这个类。
int main()
{HL::Less<int> l;cout << (l(11, 22)) << endl;return 0;
}
greater
与less类似,greater
是判断大的(就是,返回的是 x>y)。
知道了仿函数,再回头看一下**priority_queue
** 第三个模板参数,这个模板参数就是来控制大小堆的(默认是less
,就是大堆;如果我们传greater
就是小堆。)
这样我们再修改一下,向上调整算法和向下调整算法,让我们可以通过这个模板参数来控制大小堆。
//向下调整算法void AdjustDown(int parent){Compare com;int child = 2 * parent + 1;while (child < _con.size()){//if (child < _con.size() - 1 && _con[child] < _con[child + 1])if (child < _con.size() - 1 && com(_con[child], _con[child + 1])){child++;}//if (_con[parent] < _con[child])if (com(_con[parent] , _con[child])){std::swap(_con[parent], _con[child]);}else{break;}parent = child;child = 2 * parent + 1;}}//向上调整算法void AdjustUp(int child){Compare com;int parent = (child - 1) / 2;while (parent >= 0){//if (_con[parent] < _con[child])if (com(_con[parent] , _con[child])){std::swap(_con[parent], _con[child]);}else{break;}child = parent;parent = (child - 1) / 2;}}
这里测试一下,使用模拟实现的堆进行排序,pq1建大堆,排降序;pq2建小堆,排升序。
void test_priority_queue()
{HL::priority_queue<int,vecotr<int>, HL::Less<int>> pq1;pq1.push(1);pq1.push(7);pq1.push(3);pq1.push(5);pq1.push(9);while (!pq1.empty()){cout << pq1.top() << " ";pq1.pop();}cout << endl;int arr[] = { 1,3,5,7,9,8,6,4,2 };HL::priority_queue<int,vector<int>,HL::Greater<int>> pq2(arr, arr + sizeof(arr) / sizeof(arr[0]));while (!pq2.empty()){cout << pq2.top() << " ";pq2.pop();}cout << endl;
}
int main()
{test_priority_queue();return 0;
}
9 7 5 3 1
1 2 3 4 5 6 7 8 9
到这里,priority_queue
使用以及模拟实现就完成了,感谢各位的支持。
继续加油!!!
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws
Greater<int>> pq2(arr, arr + sizeof(arr) / sizeof(arr[0]));while (!pq2.empty()){cout << pq2.top() << " ";pq2.pop();}cout << endl;
}
int main()
{test_priority_queue();return 0;
}
9 7 5 3 1
1 2 3 4 5 6 7 8 9
到这里,priority_queue
使用以及模拟实现就完成了,感谢各位的支持。
继续加油!!!
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws