栈和队列

栈和队列(理论+实践)

基本概念

  • 栈和队列的逻辑结构是:线性表

  • 概念:限制存取点的后入先出的线性表

  • 数学性质:n个不同的元素入栈,出栈元素的不同排列个数是1/(n+1)*Cn2n:卡特兰数

  • 存储结构

    • 顺序栈:数组+栈顶指针

      • //创建
        int stk[N],tt;
         
        //插入操作
        stk[++tt]=x;
         
        //弹出操作(删除栈顶元素)
        tt--; 
         
        //判断栈是否为空
        if(tt>0) not empty
        else empty
         
        //取出栈顶元素
        stk[tt];
        
      • 共享栈:两个顺序栈共享一个数组空间,两个栈的栈底分别置于共享空间的两端,栈顶向中间延伸

    • 链栈:便于节点插入与删除,入栈和出栈操作都在表头进行

  • 理论题

    • 给出一段入栈序列,求各种可能的出栈序列:按照栈先入后出的特性,模拟一下入栈和出栈的过程即可
    • 前中后缀表达式的转化:step1:按优先级把所有运算单位加上括号;step2:把运算符号移动到对应括号的前面或后面;step3:去掉括号
  • 实践题

    • 匹配问题:n对数据的序列,每一对数据有前后两元素,下一对的前元素不得在这一对的后元素之前出现
      • 括号匹配问题:将{入栈。栈顶为{,入栈元素为}时弹出栈顶,若栈顶与入栈元素不匹配直接false
      • 连线不交叉问题:将每一组数据较小的一个当做左括号,另一个当做右括号,解法同括号匹配一样
    • 后缀表达式求值:数字直接入栈,算术符号则取栈顶两个数字,计算后将新数字入栈
    • 中缀表达式转后缀:数字直接入后缀表达式,' ( '入栈,' ) ':以此把栈中运算符入后缀表达式,直到出现' ( ',其他运算符:优先级小于等于栈顶则不断弹出栈顶入后缀表达式,优先级大于栈顶则入栈
    • 递归问题:递归函数的底层实现依赖于栈,理论上来说任一递归函数都可以改写为用栈来实现
    • 两个栈实现一个队列:一个栈为instack,一个栈为outstack,插入数据时放入instac,输出数据时输出outstack的栈顶,若outstack为空则将instack的所有元素放入outstack中:两个后入先出即实现先入先出
    • 火车调度问题
      • 火车调度问题I火车编号1~n,给出入栈序列,求最大字典序的出栈序列:把编号n前的火车全部入栈(n一定第一个出栈),然后用后缀数组判断n之后每一个即将入栈的元素post[i]是否大于栈顶,若大于栈顶则将元素入栈直至post[i]小于栈顶,开始弹出栈顶直到栈顶又小于post[i]
      • 火车调度问题II火车编号1~n,入栈序列为1,2,3,4……n,给出出栈序列,判断此段序列是否合法:判断栈顶元素是否是序列开头的数A,是的话弹出栈顶;不是的话看数A是否还没入栈,是的话把数A前的数全入栈,若栈顶不是A且数A已经入栈,说明出栈序列不合法
    • 单调栈问题
      • 接雨水问题:栈中存储柱子数组的下标,当入栈元素>栈顶元素,则进行一次面积计算,并弹出栈顶,实际上会形成从栈顶到栈底的一个递增单调栈
      • 给出一段序列,返回每个数左边最近的比其小的数:循环(如果入栈元素<栈顶元素),则不断弹出栈顶 ,因为新入栈元素比栈顶元素小且更靠近下一个数,所以那些元素必不会作为答案输出,弹出结束后每次取出栈顶即为每次的答案,实际上会形成从栈顶到栈底的一个递增单调栈

队列

  • 概念:操作受限的先入先出的线性表

  • 存储结构

    • 顺序队列:数组+队头指针+队尾指针

      • //在队尾插入元素,在队头弹出元素
        int q[N],front,rear=-1;
         
        //插入元素
        q[++rear]=x;
         
        //弹出队头
        front++;
         
        //取出队头
        q[front];
        
        //判断队列是否为空
        if(front<=rear) not empty
        else empty
        
      • 循环队列:顺序队列+队头指针front==maxsize-1的时候,再进一个位置到0实现循环

        • //在队尾插入元素,在队头弹出元素
          int q[N],front,rear;
          
          //插入元素
          q[rear]=x;
          rear=(rear+1)%maxsize;
          
          //弹出队头
          front=(front+1)%maxsize;
          
          //取出队头
          q[front];
          
          //判断队列是否为空
          if(front!=rear) not empty
          else empty
          
    • 链式队列:适用于数据元素变动比较大的情况

    • 双端队列:允许两端都可以入队和出队的队列

  • 理论题:依照概念和队列先入先出的特性模拟入队和出队即可

  • 实践题

    • 遍历问题:层序遍历与宽度优先搜索都依赖于队列实现,详见树篇与图论篇

    • 滑窗算法:在给定的一段序列中,动态地维护一段区间,(在有限制的条件下动态地遍历各个区间)

      • 单调队列问题
        • 一段序列上有一个定长的滑动窗口,每次返回窗口内最大的值:while(如果入队元素>队尾元素) 则不断删除队尾(rear--),因为新入队元素比队尾的元素更大且更晚出窗口,所以那些元素永远不会作为答案输出,每次的队头即为答案,实际上会形成从队尾到队头的一个递增单调队列
      • 求给定序列上,长度<=k且在和最大的情况下起始位置尽可能小的子序列,返回该序列起止坐标:将序列元素依次入队,限制条件1:要求最大和,所以当sum小于0时,要不断弹出队首,清空序列;限制条件2:当队列长度大于k时,要弹出队首;限制条件3:当队列有两个及以上元素时,如果队首小于0,就要弹出队首,每次弹出队首或新元素入队后更新sum并检查是否要更新maxsum和答案
posted @ 2022-11-22 11:30  pinoky  阅读(74)  评论(0编辑  收藏  举报