栈和队列

基本概念

  • 栈和队列都属于操作受限的线性结构
  • 栈特点:后进先出(LIFO),且只允许在一端进行插入和删除操作的线性表
  • 队列特点:先进先出(FIFO),且只允许在一端进行插入,另一端进行删除的线性表

顺序栈

  1. 进栈
      栈不满时,先栈顶指针 S.top 先上移一位再送值到栈顶元素 (先移动再赋值)
  2. 出栈
      栈非空时,先取栈顶元素值再将栈顶指针S.top减1 (先取值再移动)

共享栈

  将两个栈的栈顶对接,栈底分别设置在共享空间的两端

  1. 进栈(左栈)
      左栈顶指针先加1再赋值
  2. 进栈(右栈)
      右栈顶指针先减1再赋值

链式栈

  可以看作一个不带头结点的单链表
!链式栈通常不会出现满栈情况

进栈

!类似于单链表的头插法

  1. 给压入栈的结点p赋值 p->data = x;
  2. 将结点p的后继指针连在头指针所指结点上 p->next = S.top;
  3. 将头指针前移指向新结点p S.top = p;

出栈

  1. 找到要弹出的栈顶结点p p = S.top;
  2. 取出栈顶结点p的值 x = p->data;
  3. 将栈顶结点后移 S.top = S.top->next;
  4. 释放取出的结点p delete p;

时间复杂度与排序序列

  • 栈的出栈,入栈时间复杂度均为 \(O(1)\)
  • \(n\) 个不同元素依次进栈,则能得到 \(\frac{1}{n+1}C_{2n}^n\) 种不同的出栈序列

队列

!队首指针指向队首元素,队尾指针指向队尾元素的下一个位置

顺序队列

  1. 入队
      先赋值再将值送进队尾,队尾指针后移
  2. 出队
      先取值再把队首元素弹出,队首指针后移

假溢出

  当队首指针指向队尾元素,而队首元素前面位置还有空余
!假溢出的原因:队列出队、入队的头指针与尾指针只相加不相减,会造成删除的空间永远无法重新利用

循环队列

  为了克服假溢出时需大量移动数据元素
!循环队列是一种顺序存储结构

  1. 入队
      队尾指针后移 Q.rear = (Q.rear + 1) % MaxSize
  2. 出队
      队首指针后移 Q.front = (Q.front+ 1) % MaxSize

区分队空和队满的三种处理方式

  • 增设表示元素个数的数据成员
  • 增设tag数据成员,以区分队满还是队空
  • 牺牲一个单元来区分队空和队满

链式队列

  可以看作一个带头结点带尾指针的单链表

  1. 入队
    !类似于单链表的尾插法
    1. 给将要入队的结点p赋值 p->data = x;
    2. 给结点p创建尾指针并置空 p->next == nullptr;
    3. 将结点p接入链尾 Q.rear->next == p;
    4. 将尾指针后移并指向结点p Q.rear == p
  2. 出队
    1. 取出结点p的值 x = p->data;
    2. 将队首指针后移 Q.front->next = p->next;
    3. 释放取出的结点p delete p;

适合做链式队列的链表

  • 只带队尾指针的循环单链表
  • 带队首与队尾指针的非循环单链表
  • 只带队首或队尾指针的循环双链表
    !单向循环链表比双向循环链表更适合做队列链表结构

时间复杂度

  • 设循环单链表表示的循环队列长度为n:
  • 若只有尾指针,则出队、入队时间复杂度均为 \(O(1)\)
  • 若只有头指针,不含头结点则出队、入队均为 \(O(n)\)
    !出队:第一个元素出队后,指针后移,因其为循环链表则需找到队尾元素将尾指针与第一个元素相连
  • 若只有头指针且含头结点,则出队为 \(O(1)\),入队为 \(O(n)\)

判空/满条件

  1. 顺序栈
    • 判空条件:S.top == -1;
    • 满栈条件:S.top == MaxSize - 1;
    • 队列元素个数:S.top + 1;
  2. 共享栈
    • 判满条件:top0 + 1 == top1;
      !即判断左栈栈顶指针 +1 是否等于右栈栈顶指针,两栈顶指针指相减的绝对值是否等于 1
  3. 链式栈
    • 判空条件:S.top == nullptr;
  4. 顺序队列
    • 判空条件:Q.front == Q.rear;
  5. 循环队列
    • 判空条件:Q.front == Q.rear;
    • 队满条件:Q.front == (Q.rear + 1) % MaxSize;
    • 队列元素个数:(Q.rear + MaxSize - Q.front) % MaxSize;
  6. 链式队列
    • 判空条件:Q.front == Q.rear;

栈与队列的应用

栈的应用

  • 递归:包含两部分,递归表达式(递归体)和边界条件(递归出口)
  • 表达式求值(中缀表达式转化为前/后缀表达式)
    1. 按运算符优先级对所有运算符和它的运算数加括号(原有括号不用加)
    2. 把运算符移到对应的括号前/后
    3. 去掉括号
  • 中缀转后缀运算符的进出栈:
      遇到除数字外的字符则进栈。若栈中 top 的运算符优先级比将要进栈的运算符高,则将高优先级运算符弹出并把低优先级运算符压入栈;若将要进栈的运算符优先级和 top 运算符优先级一样,则把将要进栈的运算符弹出
  • 括号匹配
  • 非递归实现二叉树先中后序遍历
  • 迷宫求解
  • 进制转换
  • 处理函数或过程调用
  • 函数局部变量的存储

队列的应用

  • 二叉树的层序遍历
  • 图的广度优先搜索
  • 缓冲区的实现
  • 页面置换算法
posted @ 2021-11-06 20:13  絵守辛玥  阅读(105)  评论(0)    收藏  举报