从零开始学C++之STL(十一):容器适配器(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.

从零开始学C++之STL(十一):容器适配器(stack、 queue 、priority_queue)源码浅析与使用示例_第1张图片



(一)、stack

首先来看示例代码:

 C++ Code 
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 
#include 
#include 
#include 

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     //{
     //  cout<     //  s.pop();
     //}

     while (!s.empty())
    {
        cout << s.top() <<  ' ';
        s.pop();
    }
    cout << endl;
     return  0;
}

再看stack 的源码:

 C++ Code 
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

先来看示例代码:

 C++ Code 
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 
#include 
#include 
#include 
#include 

using  namespace std;

int main( void)
{
     //int a[] = {1, 2, 3, 4, 5};
     //vector 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 源码:

 C++ Code 
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

先来看示例代码:

 C++ Code 
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 
#include 
#include 
#include 
#include 
#include 

using  namespace std;

int main( void)
{
     int a[] = { 51243};
    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 的源码:

 C++ Code 
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,在构造函数中

调用make_heap函数构造二叉堆,comp 主要是用于构造二叉堆时的判别,如果是less 则构造大堆,如果传递greater 则构造小堆.

注意,priority_queue 不能用list 实现,因为list 只支持双向迭代器,而不支持随机迭代器。


下面举个例子说明make_heap 函数的用法:

 C++ Code 
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 
#include 
#include 
#include 
#include 
#include 

using  namespace std;

int main( void)
{
     int a[] = { 51243};
    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());
    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 默认都是从小到大排序,除非重载的版本传递了第三个参数,如下,第三个参数可以是函数指针,也可以是函数对象:

 C++ Code 
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++之STL(十一):容器适配器(stack、 queue 、priority_queue)源码浅析与使用示例_第2张图片


参考:

C++ primer 第四版
Effective C++ 3rd
C++编程规范


你可能感兴趣的:(从零开始学C++)