【C++】10.stack和queue

1.stack和queue

stack:栈

queue:队列

两者都是容器适配器

通过容器的push_back等功能来实现

容器适配器 都不支持迭代器遍历 因为他们通常都包含一些特殊性质

如果支持迭代器随便遍历 那他们无法很好的保持他的性质

2.stack/queue的实现/使用

#include <vector>
#include <list>
#include <iostream>
#include <queue>
#include <functional>
using namespace std;

namespace szh
{
    template<class T,class Container>//容器的类型 这个Container我也不知道 传什么就用什么
    class stack
    {
    public:
        void push(const T& x)
        {
            _con.push_back(x);
        }

        void pop()
        {
            _con.pop_back();
        }

        size_t size()
        {
            return _con.size();
        }

        bool empty()
        {
            return _con.empty();
        }

        T& top()
        {
            return _con.back();
        }
    private:
        Container _con;
    };

    void test_stack()
    {
        //stack<int, vector<int>> st;//利用模板传vector
        //stack<int, list<int>> st;//利用模板传list 两者都可以实现
        stack<int, deque<int>> st;
        st.push(1);
        st.push(2);
        st.push(3);
        st.push(4);

        while (!st.empty())
        {
            cout << st.top() << " ";
            st.pop();
        }
        cout << endl;
    }
}
  • 模板中定义一个Container的参数 用来表示容器
  • 这样就可以利用模板传容器
  • 直接调用容器的函数
  • 所有的内置功能都是容器的相关函数

 queue同理

#include <vector>
#include <list>
#include <iostream>
#include <deque>
using namespace std;

namespace szh
{
    template<class T, class Container>//容器的类型 这个Container我也不知道 传什么就用什么
    class queue
    {
    public:
        void push(const T& x)
        {
            _con.push_back(x);
        }

        void pop()
        {
            _con.pop_front();
        }

        size_t size()
        {
            return _con.size();
        }

        bool empty()
        {
            return _con.empty();
        }

        T& front()
        {
            return _con.front();
        }

        T& back()
        {
            return _con.back();
        }
    private:
        Container _con;
    };

    void test_queue()
    {
        //queue<int, vector<int>> q;//vector不行 库里面的vector不支持头删 时间复杂度太高
        //queue<int, list<int>> q;
        queue<int, deque<int>> q;
        q.push(1);
        q.push(2);
        q.push(3);
        q.push(4);

        while (!q.empty())
        {
            cout << q.front() << " ";
            q.pop();
        }
        cout << endl;
    }
}

总结一下:stl中的stack和queue是通过容器适配转换出来的 不是原生实现的 为什么呢? ->

复用 自己写一个容器适配也是可以的 所以没有写死

库里面是用deque来适配的

3.deque

deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾

两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬

移元素;与list比较,空间利用率比较高。

  • 看起来好像可以替代vector和list的容器 但是实际呢?

排序效率相比vector较慢 不完美

下面测试一下效率

void dvcompare()
{
    deque<int> d;
    vector<int> v;
    const int n = 100000;
    int* a1 = new int[n];
    int* a2 = new int[n];
    
    srand(time(0));
    for (size_t i = 0; i < n; ++i)
    {
        int x = rand();
        d.push_back(x);
        v.push_back(x);
    }

    size_t begin1 = clock();
    sort(d.begin(), d.end());
    size_t end1 = clock();

    size_t begin2 = clock();
    sort(v.begin(), v.end());
    size_t end2 = clock();

    cout << end1 - begin1 << endl;
    cout << end2 - begin2 << endl;
    //差了四倍左右
    //deque慢
    //vector快
    //所以deque替代不了vector 效率低了
}

使用

void test_deque()
{
    deque<int> d;
    d.push_back(-1);
    d.push_back(1);
    d.push_back(2);
    d.push_back(5);
    d.push_back(0);
    d.push_front(10);

    for (size_t i = 0; i < d.size(); ++i)
    {
        cout << d[i] << " ";
    }
    cout << endl;
}

  • deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组。
  • 双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂
  • 上面谈到效率不如vector 为什么要用deque?那为什么栈和队列的适配又可以使用呢?
  • 头尾的插入删除效率还是可以的 stack和queue没有用到随机访问 所以可以用
  • deque就是双端开口 可进可出

实际上deque的结构 如下图

  • 设计4个指针 类似小数组的组合

  • 如何管理多个小数组buffer呢?

    通过中控映射的方式 中控管理的指针数组 每个指针指向一个小数组

  • 如何实现opeartor[]的随机访问呢?

    通过指针的移动来访问数据

    需要计算访问的第i个数据在哪个buffer

    当数据量大了以后 效率就变低了

4.priorty_queue

优先级队列 底层就是堆 默认是大堆 但是缺省符号给了小于 小堆需要改变符号 变成大于

那么默认是大堆 如果想建小堆 那么需要一个一个换符号 很麻烦

这时模板的优势就来了 再增加一个模板参数 控制大于号还是小于号

//默认升序 怎么排降序 通过仿函数比较大小
void test_sort()
{
    vector<int> v;
    v.push_back(5);
    v.push_back(1);
    v.push_back(2);
    v.push_back(4);
    
    //升序less <
    sort(v.begin(), v.end());

    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;

    //降序 greater >
    //greater<int> gt;
    //sort(v.begin(), v.end(), gt);
    sort(v.begin(), v.end(), greater<int>());
    for (auto e : v)
    {
        cout << e << " ";
    }
    cout << endl;
}

void test_priority_queue()
{
    //priority_queue<int> pq;//默认大的优先级高 //默认是大堆
    priority_queue<int,vector<int>,greater<int>> pq;//如果想变成小的优先级高 如何操作? 仿函数 #include <functional>
    pq.push(3);
    pq.push(1);
    pq.push(9);
    pq.push(4);
    pq.push(2);

    while (!pq.empty())
    {
        cout << pq.top() << " ";
        pq.pop();
    }
    cout << endl;
}

#include <iostream>
#include <vector>
using namespace std;

namespace szh
{
    //默认是大堆
    template<class T, class Container = vector<T>,class Compare = less<T>>//标准库默认是大堆 但是缺省给了小于
    class priority_queue//默认vector 底层是堆 logN 二叉树
    {
    public:
        //从小到大就是符号变一下 其他都一样 有没有很好的方式取控制符号?
        template<class T>
        struct less
        {
            //仿函数 函数对象
            bool operator()(const T& x1, const T& x2)
            {
                return x1 < x2;
            }
        };

        template<class T>
        struct greater
        {
            //仿函数 函数对象
            bool operator()(const T& x1, const T& x2)
            {
                return x1 > x2;
            }
        };
        //向上调整
        void AdjustUp(int child)
        {
            Compare com;
            int parent = (child - 1) / 2;
            while (child > 0)//child==0的时候就不用调了
            {
                //if (_con[child] > _con[parent])
                if (com(_con[parent], _con[child]))//换顺序 默认是小于
                {
                    swap(_con[parent], _con[child]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else
                {
                    break;
                }
            }
        }

        //向下调整
        void AdjustDown(int root)
        {
            Compare com;
            int parent = root;
            int child = parent * 2 + 1;
            while (child < _con.size())
            {
                //选出左右孩子中大的那一个
                //if (child + 1 < _con.size() && _con[child + 1] > _con[child])
                if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))//换顺序 默认是小于号
                {
                    ++child;
                }
                //if (_con[child] > _con[parent])
                if (com(_con[parent], _con[child]))
                {
                    swap(_con[child], _con[parent]);
                    parent = child;
                    child = parent * 2 + 1;
                }
                else
                {
                    break;
                }
            }
        }
        void push(const T& x)
        {
            _con.push_back(x);
            AdjustUp(_con.size() - 1);//向上调整 二叉树处

        }
        void pop()
        {
            swap(_con[0], _con[_con.size() - 1]);
            _con.pop_back();

            AdjustDown(0);//向下调整
        }
        T& top()
        {
            return _con[0];
        }
        size_t size()
        {
            return _con.size();
        }
        bool empty()
        {
            return _con.empty();
        }
    private:
        Container _con;
    };

    void test_priority_queue()
    {
        //priority_queue<int> pq;//大堆
        priority_queue<int,vector<int>,greater<int>> pq;//大于反而是小堆
        pq.push(3);
        pq.push(1);
        pq.push(9);
        pq.push(4);
        pq.push(2);

        while (!pq.empty())
        {
            cout << pq.top() << " ";
            pq.pop();
        }
        cout << endl;
    }
}

5.总结

  • 容器:string/vector/list/deque(序列式容器)

  • 适配器:stack/queue/priority_queue

  • 迭代器:iterator/const_iterator/reverse_iterator/const_reverse_iterator

  • 算法:sort/find/reverse

  • 仿函数:less/greater

  • deque的缺点:

    大量的频繁opeartor[]的效率低

    迭代器的遍历相对复杂 效率也有一些影响

  • vector缺点:

    头插头删的效率低 空间不够用了增容代价大

  • list缺点:

    不支持随机访问

  • priority_queue 优先级队列 大的小的谁优先级高是看场景的

    #include<queue> 包了队列和优先级队列

  • 面试问题:能否设计出一个数据结构 解决vector和list的缺点

    deque中控映射指针数组不够了增容就可以

    但对比vector 一起去堆相同的10w个数据排序 deque效率相比vector差了4-5倍

【C++】10.stack和queue 完

猜你喜欢

转载自blog.csdn.net/szh0331/article/details/129540579