• 栈和队列


    栈和队列都属于限制了插入、删除操作的表。栈要求插入、删除操作都只能作用在一端;队列要求插入、删除操作不能作用在同一端。

    因此,栈是先进后出的一种数据结构,队列是先进先出的数据结构。

    C++的标准库中有栈模板,它的详细操作介绍请参考这篇博文:http://www.cnblogs.com/yeqluofwupheng/p/6711265.html

    下面从算法和应用等方面介绍栈。

    顺序栈和链栈

    栈的实现可以是建立在数组或链表上的,建立在数组上的顺序栈和建立在链表上的链栈也有一些区别:

    • 在顺序栈中,定义了栈的栈底指针(存储空间首地址base)、栈顶指针top以及顺序存储空间的大小stacksize;而对于链栈来说,它只定义栈顶指针。
    • 顺序栈和链栈的top指针有区别,顺序栈的top指针指向栈定的空元素处,top-1才指向栈顶元素,而链栈的top指针相当于链表的head指针一样,指向实实在在的元素。
    • 在空间上,顺序表是静态分配的,而链表则是动态分配的;就存储密度来说:顺序表等于1,而链式表小于1。
    • 顺序栈由于有栈空间大小,所以一般栈中的空间是有限制的,而链栈是动态分配,它的空间大小等于可用的整个内存空间。

    链栈的简单实现:

    class LinkStack{
    public:
        bool isEmpty(){ return !head; }
        void push(int val){
            ListNode *r = new ListNode(val);
            r->next = head;
            head = r;
        }
        void pop(){
            if (isEmpty())return;
            ListNode *p = head;
            head = head->next;
        }
        int top(){
            if (isEmpty())return -1;//抛出异常
            return head->val;
        }
    private:
        ListNode *head = nullptr;
    };

    Min Stack

    LeetCode中有一道题,设计最小栈Min Stack。支持push、top、pop、getMin这四种操作。

    • push(x) -- 将x入栈.
    • pop() -- 栈顶元素出栈.
    • top() -- 得到栈顶元素的值.
    • getMin() -- 返回栈中最小值的元素.

    使用双栈来实现最小栈

    class MinStack {
    public:
        /** initialize your data structure here. */
        MinStack() {
    
        }
    
        void push(int x) {
            if (s.empty() || s.top().second > x){//需要更新最小值
                s.push(make_pair(x, x));
            }
            else{//上一个的最小值是当前的最小值
                s.push(make_pair(x, s.top().second));
            }
        }
    
        void pop() {
            s.pop();
        }
    
        int top() {
            return s.top().first;
        }
    
        int getMin() {
            return s.top().second;
        }
    private:
        stack<pair<int,int>>s;//first存当前位置的实际值,second存当前位置的最小值
    };

    Implement Stack using Queues

    使用队列来实现栈,要求包含下面的操作:

    • push(x) -- 将x入栈.
    • pop() -- 栈顶元素出栈.
    • top() -- 得到栈顶元素的值.
    • empty() -- 判断栈是否为空.
    class MyStack {
    public:
        /** Initialize your data structure here. */
        MyStack() {
    
        }
    
        /** Push element x onto stack. */
        void push(int x) {
            Q.push(x);
            for (size_t i = 1; i < Q.size(); i++){
                Q.push(Q.front());
                Q.pop();
            }
        }
    
        /** Removes the element on top of the stack and returns that element. */
        int pop() {
            int v = Q.front();
            Q.pop();
            return v;
        }
    
        /** Get the top element. */
        int top() {
            return Q.front();
        }
    
        /** Returns whether the stack is empty. */
        bool empty() {
            return Q.empty();
        }
    private:
        queue<int>Q;
    };

    栈的应用

    数制转换:即将十进制转换为K进制;

    括号匹配:{}、()、[]三种括号的嵌套和匹配。

    表达式求值:给定一个算数表达式字符串,求出它的值。

    中缀与后缀表达式的转换:将一个中缀表达式转换成后缀表达式。

    队列

    Implement Queue using Stacks

    使用栈实现队列,要求包含下面操作:

    • push(x) -- 将x入队.
    • pop() -- 队列的头部的元素出队.
    • peek() -- 查看队列的头部的元素.
    • empty() -- 返回队列是否为空.

    双栈实现队列。

    class MyQueue {
    public:
        /** Initialize your data structure here. */
        MyQueue() {
    
        }
    
        /** Push element x to the back of queue. */
        void push(int x) {
            sin.push(x);
        }
    
        /** Removes the element from in front of queue and returns that element. */
        int pop() {
            int v = 0;
            if (sout.empty()){
                while (!sin.empty()){
                    sout.push(sin.top());
                    sin.pop();
                }
            }
            v = sout.top();
            sout.pop();
            return v;
        }
    
        /** Get the front element. */
        int peek() {
            if (sout.empty()){
                while (!sin.empty()){
                    sout.push(sin.top());
                    sin.pop();
                }
            }
            return sout.top();
        }
    
        /** Returns whether the queue is empty. */
        bool empty() {
            return sin.empty() && sout.empty();
        }
    private:
        stack<int>sin,sout;
    };

    队列的应用

    例如,打印机中的任务队列,售票时每个窗口的队列,系统中的消息队列等,很多场景需要用到队列这一结构。

    双端队列

    双端队列对应C++标准库中的deque容器,双端队列是一种具有队列和栈的性质的数据结构。双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。

    deque采用分块的线性存储结构来存储数据,每块的大小一般为512B,将之称为deque块,所有的deque块使用一个map块进行管理,每个map数据项记录各个deque块的首地址。这样的话,deque块在头部和尾部都可以插入和删除,而不需要移动其他元素(使用

    push_back()方法在尾部插入元素,会扩张队列,而使用push_front()方法在首部插入元素和使用insert()方法在中间插入元素,只是将原位置上的元素进行覆盖,不会增加新元素)一般来说,当考虑到容器元素的内存分配策略和操作的性能时deque相当于vector更有优势。

    deque的构造方法

    • deque<type> deq                                        创建一个没有任何元素的双端队列
    • deque<type> deq(otherDeq)                    用另一个类型相同双端队列初始化该双端队列
    • deque<type> deq(size)                              初始化一个固定size的双端队列
    • deque<type> deq(n, element)                  初始化n个相同元素的双端队列
    • deque<type> deq(begin,end)                   初始化双端队列中的某一段元素,从begin 到 end - 1

    deque的操作

    • deq.assign(n,elem)               赋值n个元素的拷贝给双端队列
    • deq.assign(beg,end)            赋值一段迭代器的值给双端队列
    • deq.push_front(elem)           添加一个元素在开头
    • deq.pop_front()                       删除第一个元素
    • deq.at(index)                           取固定位置的元素
    • deq[index]                                取固定位置的元素
    • deq.front()                                返回第一个元素(不检测容器是否为空)
    • deq.back()                                返回最后一个元素(不检测容器是否为空)

    输入受限的双端队列

    即:一端既能输入又能输出,另一端只能输出的双端队列;

    输出受限的双端队列

    即:一端既能输入又能输出,另一端只能输入的双端队列;

    一个经典问题:

    如果以1、2、3、4为一个双端队列的输入序列,则满足下面条件的输出序列是什么?

    1)能够通过一个输入受限的双端队列得到,但不能通过输出受限的双端队列得到;

    2)能够通过一个输出受限的双端队列得到,但不能通过输入受限的双端队列得到;

    3)既不能通过一个输入受限的双端队列得到,又不能通过输出受限的双端队列得到;

    输入受限的双端队列不可以得到:4213、4231

    输出受限的双端队列不可以得到:4132、4231

    因此上面的答案分别是

    1)4132;2)4213;3)4231

  • 相关阅读:
    Nginx服务器环境搭建
    PostgreSQL常见问题处理方法
    Linux之awk使用
    PostgreSQL常用SQL
    用apache commons-pool2建立thrift连接池
    redis开发小结
    如何解决netty发送消息截断问题
    后端服务开发总结
    利用git reflog找回错误的重置
    TCP长链接调试利器nc
  • 原文地址:https://www.cnblogs.com/yeqluofwupheng/p/7420675.html
Copyright © 2020-2023  润新知