# 栈和队列的定义和特点
# 栈的定义和特点
- 定义:栈式限定于插入和删除只能在表的尾端点进行的线性表
- 特点:先入后出
- 栈的相关概念:
- 栈顶 Top:插入元素插入到栈顶,称为入栈,删除元素删除栈顶元素,称为出栈
- 栈底 Base:最先入栈的元素
# 队列的定义和特点
- 定义:队列限定于插入元素插入在队尾,删除元素删除在对头
- 特点:先进先出
- 队列的相关概念:
- 队头:删除元素在队头进行操作
- 队尾:添加元素在队尾进行操作
# 栈和队列的比较
栈 | 队列 | |
---|---|---|
定义 | 限定只能在表的一端进行插入和删除运算的线性表(只能在栈顶操作) | 只能的表的一端进行插入运算,在表的另外一端进行删除运算(头删尾插) |
逻辑结构 | 与线性表相同,仍为一对一关系 | 与线性表相同,仍为一对一关系 |
存储结构 | 用顺序栈或链栈存储均可 | 顺序队和链队均可 |
运算规则 | 只能在栈顶运算,且访问节点时依照后进先出(LIFO)原则 | 只能在队首和队尾运算,且访问节点时依照先进先出(FIFO)原则 |
实现方式 | 关键式编写入栈和出栈的函数 | 关键掌握入队和出队操作 |
# 栈的表示和操作实现
# 栈的抽象数据类型定义
ADT Stack {
数据对象:定义对象结构
数据关系:规定栈顶,栈底元素
基本操作:初始化,进栈,出栈,取栈顶元素等
} ADT Stack
# 栈的基本操作思路
- InitStack(&S):
- 操作结果:构造一个空栈 S
- DestroyStack(&S):
- 初始条件:栈 S 已经存在
- 操作结果:栈 S 被销毁
- StackEmpty(S):
- 初始条件:栈 S 已经存在
- 操作结果:若栈 S 为空栈,则返回
true
,否则false
- StackLength(S):
- 初始条件:栈 S 已经存在
- 操作结果:返回 S 的元素个数,即栈的长度
- GetTop(S,&e):
- 初始条件:栈 S 已经存在
- 操作结果:用 e 返回 S 的栈顶元素
- ClearStack(&S)
- 初始条件:栈 S 已经存在
- 操作结果:将 S 清为空栈
- Push(&S,e):
- 初始条件:栈 S 已经存在
- 操作结果:插入元素 e 为新的栈顶元素
- Pop(&S,&e):
- 初始条件:栈 S 已经存在且非空
- 操作结果:删除 S 的栈顶元素,并用 e 返回其值
# 顺序栈
- 存储方式:同一线性表的顺序存储结构完全相同
- 栈顶指针(top):指示栈顶元素在顺序栈中的位置
- 栈底指针(base):指示栈底元素在顺序栈中的位置(为了方便入栈操作,通常 top 指向栈顶元素之上的下标地址)
- 用 stacksize 表示栈可使用的最大容量
- 空栈:base == top 是栈空的标志
- 栈满:top - base == stacksize
- 栈满时处理方法:
- 报错,返回操作系统
- 分配更大的空间,作为栈的存储空间,将原栈的内容移入新栈
- 上溢(overflow):栈已经满,又要压入元素
- 下溢(underflow):栈已经空,还要弹出元素
# 链栈
- 定义:链栈式运算受限的单链表,只能在链表头部进行操作
- 栈顶:栈顶指针即为头指针(采用头插法创建链表)
# 栈与递归
# 递归的定义
- 若一个对象部分地包含它自己,或用它自己给自己定义,则称这个对象是递归,则称这个对象是递归的
- 若一个过程直接地或间接地调用自己,则称这个过程是递归的过程
- 分治法:对于一个复杂的问题,能够分解成几个相对简单的且解法相同或类似的子问题来求解
- 优点:结构清晰,程序易读
- 缺点:每次调用都要生成工作记录,保存状态信息,入栈;返回时要出栈,恢复状态信息,时间开销大
// 递归求 n 的阶乘 | |
long Fact(long n) { | |
if(n == 0) { | |
return 1; | |
} else { | |
return n * Fact(n - 1); | |
} | |
} |
# 借助栈改写递归
- 设置一个工作栈存放递归工作记录
- 进入非递归调用入口将调用程序传来的实在参数和返回地址入栈
- 进入递归调用入口:当不满足递归结束条件时,逐层递归,将实参,返回地址及局部变量入栈,这一过程可用循环语句来实现模拟递归拆解过程
- 递归结束条件满足,将到达递归出口的给定常数作为当前函数值
- 返回处理:在栈不空的情况下,返回退出栈顶记录,根据栈顶记录的返回地址进行题意规定的操作,即逐层计算当前函数值,直至栈空为止,模拟递归求值过程
# 队列的表示和操作实现
# 相关术语
- 队列(Queue):仅在表尾进行插入操作,在对头进行删除操作的线性表
- 队尾:最后进入队列的元素
- 队头:最先进入队列的元素
- 入队:插入元素到队尾
- 出队:删除元素在队头
# 队列的抽象数据类型定义
ADT Queue {
数据对象:操作的数据的对象
数据关系:规定队头和队尾
基本操作:入队,出队,判断队列是否为空等
}
# 顺序队列
- 定义队头指针(front):
int front = 0
(每删除一个元素 front++) - 定义队尾指针(rear):
int rear = 0
(每插入一个元素 rear++,一半让 rear 指向队尾元素下标值 + 1) - 初始:
front = rear = 0
- 入队:
base[rear] = x; rear++;
- 出队:
x = base[front]; front++;
- 空队标志:
front == rear
- 发生溢出:
rear == MAXQSIZE
- 真溢出:
front == 0 && rear == MAXQSIZE
,再入队,真正溢出 - 假溢出:
front != 0 && rear == MAXQSIZE
,再入队,假溢出
# 循环队列
- 定义:为了解决普通队列的假上溢问题
- 解决方法:base [0] 接在 base [MAXQSIZE - 1] 之后,若 rear + 1 == M,则令 rear = 0
- 插入元素:
Q.base[Q.rear] = x
Q.rear = (Q.rear + 1)%MAXQSIZE
- 删除元素:
x = Q.base[s.front]
Q.front = (Q.front + 1)%MAXQSIZE
- 解决队满时判断方法:少用一个元素空间,
(rear + 1)%MAXQSIZE == front
- 队空:
front == rear
# 链式队列
- 队头:头节点指向的地址
- 队尾:最后一个节点的地址
- 优点:动态生成队列的节点,防止顺序队列中的队满,溢出