C++ STL - stack queue priority_queue 适配器

目录

容器适配器

什么是适配器

STL标准库中stack和queue的底层结构

stack

queue

priority_queue

stack和queue为什么选择deque作为底层默认容器:


容器适配器

什么是适配器

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总 结),该种模式是将一个类的接口转换成客户希望的另外一个接口。

STL标准库中stack和queue的底层结构

虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack和队列只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque,priority_queue默认使用vector作为其底层容器。

(简单来说就是,stack和queue没必要自己再实现一遍那些逻辑操作,本身需要的操作并不多,并且也没有对底层存储结构有要求,而又有了vector list deque等成熟的,功能完善的容器,所以只需要对那些容器进行简单封装即可,适配器即将其他容器适配为我们现在需要的容器,对函数等进行封装转换。)

stack

 

1. 后进先出 LIFO
2. 是一种容器适配器,只能从容器的一端进行元素的插入,提取,删除操作。
3. std::stack默认使用deque作为其适配容器,适配容器需要支持empty size push_back pop_back操作,vector list都符合

#ifndef STL_STACK_H
#define STL_STACK_H
#include <deque>
#include <cstdlib>
namespace yzl
{
    template<class T, class Container = std::deque<T>>
    class stack
    {
    public:
        void push(const T& val)
        {
            _con.push_back(val);
        }
        void pop()
        {
            _con.pop_back();
        }
        T& top()
        {
            return _con.back();
        }
        const T& top() const
        {
            return _con.back();
        }
        size_t size() const
        {
            return _con.size();
        }
        bool empty() const
        {
            return _con.empty();
        }

    private:
        // vector<T> _con;
        Container _con;
    };
}

queue

 

1. 先进先出,FIFO
2. 队列是一种容器适配器,支持front back双端获取元素,元素从队尾入队列,从队头出队列。
3. std::queue默认使用deque为容器适配器,底层容器必须支持push_back pop_front back front操作,deque,list都可以,但vector不适合做queue的适配容器。

#ifndef STL_QUEUE_H
#define STL_QUEUE_H
#include <deque>
#include <cstdlib>
namespace yzl
{
    // 容器不适于stack,因为没有pop_front
    template<class T, class Container = std::deque<T>>
    class queue
    {
    public:
        void push(const T& val)
        {
            _con.push_back(val);
        }
        void pop()
        {
            _con.pop_front();
        }
        T& front()
        {
            return _con.front();
        }
        T& back()
        {
            return _con.back();
        }
        const T& front() const
        {
            return _con.front();
        }
        const T& back() const
        {
            return _con.back();
        }
        size_t size() const
        {
            return _con.size();
        }
        bool empty() const
        {
            return _con.empty();
        }
    private:
        Container _con;
    };
}

#endif //STL_QUEUE_H

priority_queue

1. 优先级队列是一种容器适配器,它的第一个元素总是它所包含的元素中最大的(最小的)。
2. 优先级队列的数据结构就是一个堆,且默认为大堆,第三个模板参数 class Compare = std::less<T>
3. 仅支持push pop top操作,也就是priority_queue只能获取当前堆结构的堆顶元素。
4. 大堆和小堆由第三个比较参数决定,less为大堆,greater是小堆
5. 底层容器应当支持随机访问迭代器(下标随机访问),push_back pop_back front操作,致使vector和deque适合做priority_queue的适配容器,list不适合,因为不支持随机访问迭代器(随机访问)
6. std::priority_queue默认使用vector是因为vector的随机访问比deque更高效。需要支持随机访问迭代器,以便始终在内部保持堆结构。在vector上又使用了堆算法将vector中元素构造成 堆的结构

namespace yzl
{
    // less大堆,greater小堆
    template<class T, class Container = std::vector<T>, class Compare = std::less<T> >
    class priority_queue
    {
    private:
        Container _con;
    public:
        priority_queue() = default;
        template<class InputIterator>
        priority_queue(InputIterator begin, InputIterator end)
        {
            while(begin != end)
            {
                _con.push_back(*begin);
                ++begin;
            }
            // 建堆,向下调整效率高
            for(int i = (_con.size()-1-1)/2; i >= 0; --i)
            {
                adjust_down(i);
            }
        }
        void push(const T& val)
        {
            _con.push_back(val);
            adjust_up(_con.size()-1);
        }
        void adjust_up(size_t child)
        {
            Compare cmp;
            size_t parent = (child-1)/2;
            while(child > 0)  // 向上调整终止条件
            {
                if(cmp(_con[parent], _con[child]))
                {
                    std::swap(_con[parent], _con[child]);
                    child = parent;
                    parent = (child-1)/2;
                }
                else
                {
                    break;
                }
            }
        }
        void pop()
        {
            std::swap(_con[0], _con[_con.size()-1]);
            _con.pop_back();
            adjust_down(0);
        }
        void adjust_down(size_t parent)
        {
            Compare cmp;
            size_t child = parent*2+1;
            while(child < _con.size())
            {
                if(child + 1 < _con.size() && cmp(_con[child], _con[child+1]))
                {
                    child+=1;
                }
                if(cmp(_con[parent], _con[child]))
                {
                    std::swap(_con[parent], _con[child]);
                    parent = child;
                    child = parent*2+1;
                }
                else
                {
                    break;
                }
            }
        }
        // 优先级队列的top返回值为const不可以修改。
        const T& top() const
        {
            return _con.front();
        }
        size_t size() const
        {
            return _con.size();
        }
        bool empty() const
        {
            return _con.empty();
        }
    };
}

其实就是对一个容器进行堆算法,将其实现为一个堆。重点是向上调整和向下调整算法。还有建堆时,最高效的是从第一个非叶节点开始向下调整。

stack和queue为什么选择deque作为底层默认容器:

stack后进先出,只要push_back pop_back都可以,vector list deque都可以
queue先进先出,只要push_back pop_front都可以,list deque都可以

但是STL中对stack和 queue默认选择deque作为其底层容器,主要是因为:(这里的原因根本上还是vector list 和 deque的优劣比较)

1. 栈和队列不需要遍历(因此stack和queue没有迭代器),而vector是比deque遍历更快的
2. 在stack和queue元素增长时,deque比vector的效率高,因为扩容时不需要移动大量元素(基于deque的底层存储结构),并且deque不仅效率高,内存使用率也高。

deque的劣势是在中间插入删除,这是list的优势。还有元素随机访问的效率并不是太高,这也是vector的优势。而stack和queue不需要这方面。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值