栈和队列
基本概念
- 栈和队列都属于操作受限的线性结构
- 栈特点:后进先出(LIFO),且只允许在一端进行插入和删除操作的线性表
- 队列特点:先进先出(FIFO),且只允许在一端进行插入,另一端进行删除的线性表
栈
顺序栈
- 进栈
栈不满时,先栈顶指针S.top
先上移一位再送值到栈顶元素 (先移动再赋值) - 出栈
栈非空时,先取栈顶元素值再将栈顶指针S.top
减1 (先取值再移动)
共享栈
将两个栈的栈顶对接,栈底分别设置在共享空间的两端
- 进栈(左栈)
左栈顶指针先加1再赋值 - 进栈(右栈)
右栈顶指针先减1再赋值
链式栈
可以看作一个不带头结点的单链表
!链式栈通常不会出现满栈情况
进栈
!类似于单链表的头插法
- 给压入栈的结点p赋值
p->data = x;
- 将结点p的后继指针连在头指针所指结点上
p->next = S.top;
- 将头指针前移指向新结点p
S.top = p;
出栈
- 找到要弹出的栈顶结点p
p = S.top;
- 取出栈顶结点p的值
x = p->data;
- 将栈顶结点后移
S.top = S.top->next;
- 释放取出的结点p
delete p;
时间复杂度与排序序列
- 栈的出栈,入栈时间复杂度均为 \(O(1)\)
- \(n\) 个不同元素依次进栈,则能得到 \(\frac{1}{n+1}C_{2n}^n\) 种不同的出栈序列
队列
!队首指针指向队首元素,队尾指针指向队尾元素的下一个位置
顺序队列
- 入队
先赋值再将值送进队尾,队尾指针后移 - 出队
先取值再把队首元素弹出,队首指针后移
假溢出
当队首指针指向队尾元素,而队首元素前面位置还有空余
!假溢出的原因:队列出队、入队的头指针与尾指针只相加不相减,会造成删除的空间永远无法重新利用
循环队列
为了克服假溢出时需大量移动数据元素
!循环队列是一种顺序存储结构
- 入队
队尾指针后移Q.rear = (Q.rear + 1) % MaxSize
- 出队
队首指针后移Q.front = (Q.front+ 1) % MaxSize
区分队空和队满的三种处理方式
- 增设表示元素个数的数据成员
- 增设tag数据成员,以区分队满还是队空
- 牺牲一个单元来区分队空和队满
链式队列
可以看作一个带头结点带尾指针的单链表
- 入队
!类似于单链表的尾插法- 给将要入队的结点p赋值
p->data = x;
- 给结点p创建尾指针并置空
p->next == nullptr;
- 将结点p接入链尾
Q.rear->next == p;
- 将尾指针后移并指向结点p
Q.rear == p
- 给将要入队的结点p赋值
- 出队
- 取出结点p的值
x = p->data;
- 将队首指针后移
Q.front->next = p->next;
- 释放取出的结点p
delete p;
- 取出结点p的值
适合做链式队列的链表
- 只带队尾指针的循环单链表
- 带队首与队尾指针的非循环单链表
- 只带队首或队尾指针的循环双链表
!单向循环链表比双向循环链表更适合做队列链表结构
时间复杂度
- 设循环单链表表示的循环队列长度为n:
- 若只有尾指针,则出队、入队时间复杂度均为 \(O(1)\)
- 若只有头指针,不含头结点则出队、入队均为 \(O(n)\)
!出队:第一个元素出队后,指针后移,因其为循环链表则需找到队尾元素将尾指针与第一个元素相连 - 若只有头指针且含头结点,则出队为 \(O(1)\),入队为 \(O(n)\)
判空/满条件
- 顺序栈
- 判空条件:
S.top == -1;
- 满栈条件:
S.top == MaxSize - 1;
- 队列元素个数:
S.top + 1;
- 判空条件:
- 共享栈
- 判满条件:
top0 + 1 == top1;
!即判断左栈栈顶指针 +1 是否等于右栈栈顶指针,两栈顶指针指相减的绝对值是否等于 1
- 判满条件:
- 链式栈
- 判空条件:
S.top == nullptr;
- 判空条件:
- 顺序队列
- 判空条件:
Q.front == Q.rear;
- 判空条件:
- 循环队列
- 判空条件:
Q.front == Q.rear;
- 队满条件:
Q.front == (Q.rear + 1) % MaxSize;
- 队列元素个数:
(Q.rear + MaxSize - Q.front) % MaxSize;
- 判空条件:
- 链式队列
- 判空条件:
Q.front == Q.rear;
- 判空条件:
栈与队列的应用
栈的应用
- 递归:包含两部分,递归表达式(递归体)和边界条件(递归出口)
- 表达式求值(中缀表达式转化为前/后缀表达式)
- 按运算符优先级对所有运算符和它的运算数加括号(原有括号不用加)
- 把运算符移到对应的括号前/后
- 去掉括号
- 中缀转后缀运算符的进出栈:
遇到除数字外的字符则进栈。若栈中 top 的运算符优先级比将要进栈的运算符高,则将高优先级运算符弹出并把低优先级运算符压入栈;若将要进栈的运算符优先级和 top 运算符优先级一样,则把将要进栈的运算符弹出 - 括号匹配
- 非递归实现二叉树先中后序遍历
- 迷宫求解
- 进制转换
- 处理函数或过程调用
- 函数局部变量的存储
队列的应用
- 二叉树的层序遍历
- 图的广度优先搜索
- 缓冲区的实现
- 页面置换算法