容器适配器(stack、 queue 、priority_queue)源码浅析与使用示例
一、容器适配器
stack
queue
priority_queue
stack、queue、priority_queue 都不支持任一种迭代器,它们都是容器适配器类型,stack是用vector/deque/list对象创建了一个先进后出容器;queue是用deque或list对象创建了一个先进先出容器;priority_queue是用vector/deque创建了一个排序队列,内部用二叉堆实现。
前面或多或少谈到过list/vector的实现,而没提到过deque的实现,可以用以下一句话概括,具体可以看看《stl源码剖析》:
Storing contents in multiple smaller arrays, allocating additional arrays at the beginning or end as needed.
Indexing is implemented by keeping a dynamic array containing pointers to each of the smaller arrays.
(一)、stack
首先来看示例代码:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include <iostream>
#include <vector> #include <list> #include <stack> using namespace std; int main(void) { stack<int, list<int> > s; for (int i = 0; i < 5; i++) { s.push(i); } //for (size_t i=0; i<s.size(); i++) //{ // cout<<s.top()<<' '; Error:size()一直在变化 // s.pop(); //} while (!s.empty()) { cout << s.top() << ' '; s.pop(); } cout << endl; return 0; } |
再看stack 的源码:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
// TEMPLATE CLASS stack
template < class _Ty, class _Container = deque<_Ty> > class stack { // LIFO queue implemented with a container public: typedef _Container container_type; typedef typename _Container::value_type value_type; typedef typename _Container::size_type size_type; typedef typename _Container::reference reference; typedef typename _Container::const_reference const_reference; stack() : c() { // construct with empty container } explicit stack(const _Container &_Cont) : c(_Cont) { // construct by copying specified container } bool empty() const { // test if stack is empty return (c.empty()); } size_type size() const { // test length of stack return (c.size()); } reference top() { // return last element of mutable stack return (c.back()); } const_reference top() const { // return last element of nonmutable stack return (c.back()); } void push(const value_type &_Val) { // insert element at end c.push_back(_Val); } void pop() { // erase last element c.pop_back(); } const _Container &_Get_container() const { // get reference to container return (c); } protected: _Container c; // the underlying container }; |
即有一个_Container 成员,默认是deque<_Ty> ,当然也可以传递vector, list 进去,只要支持push_back,pop_back 等接口。内部的函数实现
都借助了容器的函数,跟以前实现过的Stack 很像。
(二)、queue
先来看示例代码:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include <iostream>
#include <vector> #include <list> #include <stack> #include <queue> using namespace std; int main(void) { //int a[] = {1, 2, 3, 4, 5}; //vector<int> v(a, a+5); queue<int, list<int> > q; for (int i = 0; i < 5; i++) { q.push(i); } while (!q.empty()) { cout << q.front() << ' '; q.pop(); } cout << endl; return 0; } |
再来看queue 源码:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
// TEMPLATE CLASS queue
template < class _Ty, class _Container = deque<_Ty> > class queue { // FIFO queue implemented with a container public: typedef _Container container_type; typedef typename _Container::value_type value_type; typedef typename _Container::size_type size_type; typedef typename _Container::reference reference; typedef typename _Container::const_reference const_reference; queue() : c() { // construct with empty container } explicit queue(const _Container &_Cont) : c(_Cont) { // construct by copying specified container } bool empty() const { // test if queue is empty return (c.empty()); } size_type size() const { // return length of queue return (c.size()); } reference front() { // return first element of mutable queue return (c.front()); } const_reference front() const { // return first element of nonmutable queue return (c.front()); } reference back() { // return last element of mutable queue return (c.back()); } const_reference back() const { // return last element of nonmutable queue return (c.back()); } void push(const value_type &_Val) { // insert element at beginning c.push_back(_Val); } void pop() { // erase element at end c.pop_front(); } const _Container &_Get_container() const { // get reference to container return (c); } protected: _Container c; // the underlying container }; |
实现跟stack 是很类似的,只是queue不能用vector 实现,因为没有pop_front 接口。
(三)、priority_queue
先来看示例代码:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <iostream>
#include <functional> #include <vector> #include <list> #include <stack> #include <queue> using namespace std; int main(void) { int a[] = {5, 1, 2, 4, 3}; priority_queue<int, vector<int>, greater<int> > q(a, a + 5); while (!q.empty()) { cout << q.top() << ' '; q.pop(); } cout << endl; return 0; } |
再来看priority_queue 的源码:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
// TEMPLATE CLASS priority_queue
template < class _Ty, class _Container = vector<_Ty>, class _Pr = less<typename _Container::value_type> > class priority_queue { // priority queue implemented with a _Container public: typedef _Container container_type; typedef typename _Container::value_type value_type; typedef typename _Container::size_type size_type; typedef typename _Container::reference reference; typedef typename _Container::const_reference const_reference; priority_queue() : c(), comp() { // construct with empty container, default comparator } explicit priority_queue(const _Pr &_Pred) : c(), comp(_Pred) { // construct with empty container, specified comparator } priority_queue(const _Pr &_Pred, const _Container &_Cont) : c(_Cont), comp(_Pred) { // construct by copying specified container, comparator make_heap(c.begin(), c.end(), comp); } template<class _Iter> priority_queue(_Iter _First, _Iter _Last) : c(_First, _Last), comp() { // construct by copying [_First, _Last), default comparator make_heap(c.begin(), c.end(), comp); } template<class _Iter> priority_queue(_Iter _First, _Iter _Last, const _Pr &_Pred) : c(_First, _Last), comp(_Pred) { // construct by copying [_First, _Last), specified comparator make_heap(c.begin(), c.end(), comp); } template<class _Iter> priority_queue(_Iter _First, _Iter _Last, const _Pr &_Pred, const _Container &_Cont) : c(_Cont), comp(_Pred) { // construct by copying [_First, _Last), container, and comparator c.insert(c.end(), _First, _Last); make_heap(c.begin(), c.end(), comp); } bool empty() const { // test if queue is empty return (c.empty()); } size_type size() const { // return length of queue return (c.size()); } const_reference top() const { // return highest-priority element return (c.front()); } reference top() { // return mutable highest-priority element (retained) return (c.front()); } void push(const value_type &_Pred) { // insert value in priority order c.push_back(_Pred); push_heap(c.begin(), c.end(), comp); } void pop() { // erase highest-priority element pop_heap(c.begin(), c.end(), comp); c.pop_back(); } protected: _Container c; // the underlying container _Pr comp; // the comparator functor }; |
priority_queue 的实现稍微复杂一点,可以传递3个参数,而且有两个成员,comp 即自定义比较逻辑,默认是less<value_type>,在构造函数中
调用make_heap函数构造二叉堆,comp 主要是用于构造二叉堆时的判别,如果是less 则构造大堆,如果传递greater 则构造小堆.
注意,priority_queue 不能用list 实现,因为list 只支持双向迭代器,而不支持随机迭代器。
下面举个例子说明make_heap 函数的用法:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <iostream>
#include <functional> #include <vector> #include <list> #include <stack> #include <queue> using namespace std; int main(void) { int a[] = {5, 1, 2, 4, 3}; make_heap(a, a + 5, less<int>()); copy(a, a + 5, ostream_iterator<int>(cout, " ")); cout << endl; sort(a, a + 5); // sort_heap(a, a+5, less<int>()); copy(a, a + 5, ostream_iterator<int>(cout, " ")); cout << endl; return 0; } |
输出:
5 4 2 1 3
1 2 3 4 5
make_heap() 将容器的元素构造成二叉堆,传递的是less,即构造的是大堆,把大堆层序遍历的结果存入数组,再调用sort() 进行排序,内部调用
的实际算法不一定,可以是堆排序、插入排序、选择排序等等,跟踪进去发现调用的是插入排序;当然也可以直接指定使用堆排序 sort_heap(调用
者必须已经是堆了,也就是前面已经先调用了make_heap,而且大小堆类型得匹配),与make_heap 一样,第三个参数传递的都是函数对象的用
法。sort 和 sort_heap 默认都是从小到大排序,除非重载的版本传递了第三个参数,如下,第三个参数可以是函数指针,也可以是函数对象:
1
2 3 4 5 6 7 8 |
// order heap by repeatedly popping, using operator<
template<class _RanIt> inline void sort_heap(_RanIt _First, _RanIt _Last); // order heap by repeatedly popping, using _Pred template < class _RanIt, class _Pr > inline void sort_heap(_RanIt _First, _RanIt _Last, _Pr _Pred); |
传递greater 构造的是小堆,如下图所示:
参考:
C++ primer 第四版
Effective C++ 3rd
C++编程规范