栈和队列复习提纲
栈
栈的定义:
栈是一种只能在一端进行插入或删除操作的线性表
栈的相关概念:
- 允许进行插入、删除操作的一端称为栈顶另一端称之为栈底
- 当表中没有数据元素时,称为空栈
- 栈的插入操作通常称之为进栈或者入栈
- 栈的删除操作通常称之为退栈或出栈
栈的最主要特点是后进先出(LIFO)
n个不同的元素进栈,其出栈次序总数为(Catalan number)
C2nnn+1\frac{C_{2n}^{n}}{n+1}n+1C2nn
栈的基本运算
- 初始化栈
- 销毁栈
- 判断栈是否为空
- 进栈
- 出栈
- 取栈顶元素
顺序栈
顺序栈的类型
typedef ElemType int
typedef struct
{
ElemType data[MAXSIZE];
//栈顶指针
int top;
};
顺序栈的4要素:
- 栈空条件:top == -1;
- 站满条件:top == MAXSIZE - 1;
- 进栈e操作:top++; data[top] = e;
- 出栈操作:e = data[top]; top–;
共享栈:如果要用到两个相同类型的栈,可以用一个数组来实现这两个栈,这称之为共享栈
链栈
链栈的定义:采用链表存储的栈称之为链栈,这里采用带头节点s的单链表来实现
链栈的数据节点类型
typedef ElemType int
typedef struct linknode
{
ElemType data;
struct linknode *next;
}LinkStNode;
链栈的4要素:
- 栈空条件:s->next == NULL;
- 栈满条件:不考虑
- 进栈:头插法
- 出栈:取出头节点之后的元素并删除
栈的应用举例:
- 简单表达式求值问题:用户输入一个包含“+”、“-”、“*”、“/”、正整数和圆括号的合法算术表达式,计算该表达式的运算结果。
分析:
将中缀表达式转化为后缀(逆波兰)表达式,然后对该后缀表达式求值。
转化为后缀表达式的过程:
用字符数组来存储后缀表达式,用运算符栈来确定运算符的位置。扫描中缀表达式,如果遇到数字,则直接放入字符数组,如果遇到运算符,则将其与运算符栈的符号依次进行优先级比较,如果优先级较高则直接进栈,优先级较低则一直出栈直到为空或者其优先级较高。扫描完毕,将栈中所有符号放入字符数组。
后缀表达式求值:
扫描字符数组,如果为数字,则转化为数值之后进栈,如果为运算符,则出栈两个操作数并进行计算,将结果进栈。
2. 用栈求解迷宫问题:给定一个M×N的迷宫图、入口与出口、行走规则。求一条从指定入口到出口的路径。所求路径必须是简单路径,即路径不重复。
分析:对于某一个不在边界的方块而言,与其相邻的方块的四个,如果相邻的方块可以走,则访问该方块,若某方块的周围没有可走方块,则退栈,回到上一个方块,访问下一个可以走的方块。直到访问至目标方块(参考图的深度优先搜索)
队列
队列的定义:
队列只能选取一个端点(队尾)进行插入操作,另一个端点(队头)进行删除操作
队列的相关概念:
- 进行插入的一端称之为队尾
- 进行删除的一端称之为队首或队尾
- 向队列中插入新元素称之为进队或入队,新元素进队后就成为新的队尾元素
- 从队列中删除元素称为出队或离队,元素出队后,其后继元素就成为队首元素
队列的最主要特点是后进先出(FIFO)
队列的基本运算
- 初始化队列
- 销毁队列
- 判断为空
- 进队
- 出队
顺序队列
顺序队列类型的定义
typedef ElemType int;
typedef struct
{
ElemType data[MAXSIZE];
//定义队首和队尾指针
//rear指向队尾元素
//front指向队头元素的前一个位置
int front, rear;
}SqQueue;
顺序队列的4要素
- 队空条件:rear == front;
- 队满条件:rear == MAXSIZE - 1;
- 元素e进队:rear++; data[rear] = e;
- 元素e出队:front++; e = data[front];
环形队列
在上述顺序队列中,采用rear==MAXSIZE-1为队满条件,当队满条件为空时,队中可能还有若干空位置,循环队列就可以解决这种"假溢出"的问题。
环形队列的概念:
把数组的前端和后端连接起来,形成一个环形的顺序表,即把存储队列元素的逻辑上看成一个环,这就称之为环形队列或循环队列
环形队列的4要素:
- 队空条件:front == rear;
- 队满条件:(rear + 1)%MAXSIZE == front;
- 进队e操作:rear = (rear+1)%MAXSIZE; data[rear] = e;
- 出队e操作:front = (front+1)%MAXSIZE; e = data[front];
这里要注意的是:环形队列的初始化条件是队尾指针和队首指针均赋值0,且上述环形队列由于队满条件而造成了一个位置的空缺
链队
链队的定义:
采用链表存储的队列称为链队,这里采用不带头节点的单链表实现
通常我们将队头和队尾两个指针合并起来,于是链表的组成由:
- 存储队列元素的单链表节点
- 指向队头和队尾指针的链表头节点
链队的节点类型
数据节点类型:
typedef struct qnode
{
ElemType data;
struct qnode *next;
}DataNode;
链队头节点类型
typedef struct
{
//指向队头的节点
DataNode *front;
//指向队尾的节点
DataNode *rear;
}LinkQuNode;
链队的4要素:
- 队空条件:frontrearNULL;
- 队满条件:不考虑
- 进队:尾插法
- 出队:删除首节点
链队基本运算需要注意的地方
- 进队
- 原队列非空
- 原队列为空
- 出队
- 原队列为空
- 只有一个节点
- 其他情况
队列的应用举例
- 用队列求解迷宫问题:
分析:将当前方块的所有可走方块进队列,直到找到出口点。(参考图的广度优先搜索)
关于栈和队列需要注意的一些方面
- 顺序栈的设计并不是唯一的,只要能满足栈的操作特点又能充分利用存储空间就是一种合适的设计。但最好将栈底放在数组的两端
- 用不带头节点的非循环单链表来表示链队时,不能用"队首指针和队尾指针的值相等"作为队空的标志,因为其还可能表示只有一个节点时的情况
- 环形队列不存在假溢出的问题,但仍然存在上溢出的问题