数据结构与算法基础02
王卓老师的数据结构课笔记
队列与栈
- 栈和队列也是线性表,只是限定插入和删除只能在“端点”进行的线性表。
栈
-
由于栈的操作既有后进先出的固有特性;使得栈成为程序设计中的有用工具。另外,如果问题求解 的过程具有“后进先出”的天然特性的话,则求解的算法中也必然需要利用“栈”。
-
可处理的算法问题:
- 数值转换
- 表达式求值
- 括号匹配的检验
- n皇后问题
- 行编辑程序
- 函数调用
- 迷宫求解
- 递归调用的实现
- …
-
栈(stack)是一个特殊的线性表,是限定仅在一端(通常是表尾)进行插入和删除操作的线性表。
-
又称为后进后出(Last In First Out)的线性表,简称LIFO结构。
-
栈是仅在表尾进行插入,删除操作的线性表。
-
表尾(即an端)称为栈顶Top;表头(即a1端)称为栈底Base
-
插入元素到栈顶(即表尾)的操作,称为入栈。
-
从栈顶(即表尾)删除最后一个元素的操作,称为出栈。
-
“入” = 压入 = PUSH(X),“出” = 弹出 = POP(y)
-
逻辑结构与线性表相同,仍为一对一的关系
-
存储结构:用顺序栈或链栈存储均可,但以顺序栈更常见。
-
只能在栈顶运算,且访问结点时依照后进先出(LIFO)的原则
-
实现方式:关键是编写入栈和出栈函数,具体实现依顺序栈或链栈的不同而不同。
队列
- 由于队列的操作具有先进先出的特性,使得队列成为程序员设计中解决类似排队问题的有用工具。
- 脱机打印输出:按申请的先后顺序依次输出
- 多用户系统中,多个用户排成队,分十地循环使用CPU和主存
- 按用户的优先级排成多个对,每个优先级一个队列
- 实时控制系统中,信号按照接收的先后顺序依次处理
- 网络电文传输,按照到达的时间先后顺序依次进行。
- 队列(queue)是一种先进先出(FIFO)的线性表。在表一段插入(表尾),在另一端(表头)删除。
- 通常用字母Q来表示。
- 逻辑结构:同线性表相同,仍为一对一关系
- 存储结构:顺序队或链队,以循环顺序队更为常见。
- 运算规则:只能在队首和队尾运算,且访问结点时依照先进先出(FIFO)的原则。
- 实现方式:关键是掌握入队和出队的操作,具体实现以来顺序队或链队的不同而不同。
栈和队列是线性表的子集(是插入和删除位置受限的线性表)
栈应用的典型案例
-
进制转换
-
十进制整数N向其他进制数d(二,八,十六)的转换是计算机实现计算的基本问题。
-
转化法则:除以d倒取余
n = (n div d)* d + n mod d;
其中:div为整除运算,mod为求余运算
栈的使用:后进先出的步骤来求得转换完成以后的数。
-
-
括号匹配的检验
-
假设表达式中允许包含两种括号:圆括号和方括号
-
其嵌套的顺序随意,但是要符合数学规则
栈的使用:将括号遍历进行判断,判断是否有成对的括号,先入栈的后匹配,后入栈的先匹配
-
-
表达式求值
- 是程序设计语言编辑中一个最基本的问题,他的实现也需要用栈。
- 这里介绍的算法是由运算符优先级确定运算顺序的对表达式求值的算法——算符优先算法
- 表达式的组成:
- 操作数:常数,变量。
- 运算符:算数运算符,关系运算符和逻辑运算符。
- 界限符:左右括弧和表达式结束符。
- 任何一个算数表达式都由操作数(常量、变量)、算数运算符(+、-、*、/)和界限符(括号、表达式结束符‘#’、虚设的表达式起始符‘#’)组成。后两者统称为算符。
- 为了实现表达式求值,需要设置两个栈:
- 一个是算符栈OPTR,用于寄存运算符。
- 另一个称为操作数栈OPND,用于寄存原酸数和运算结果。
- 求值的处理过程是自左向右扫描表达式的每一个字符
- 当扫描到的是运算数,则将其压入栈OPND
- 当扫描到的是运算符时
- 若这个运算符比OPTR栈顶运算符的优先级高,则入栈OPTR,继续向后处理
- 若这个运算符比OPTR栈顶运算符优先级低,则从OPND栈中弹出两个运算数,从栈OPTR中弹出栈顶运算符进行运算,并将运算结果压入栈OPND。
- 继续处理当前字符,直到遇到结束符为止。
队列应用的典型案例
-
舞伴问题
假设在误会上,男士和女士各自排成一队。舞会开始时,依次从男队和女队的队头各出一人配成舞伴。如果两队初始人数不相同,则较长的那一队中未配对者等待下一轮舞曲。现要求写以算法模拟上述舞伴配对问题。
- 显然,先入队的男士或女士先出队配成舞伴,因此该问题具有典型的先进先出特性,可以用队列作为算法的数据结构
- 首先构造两个队列
- 依次将队头元素出队配成舞伴
- 某队为空,则另外一对等待者则是下一舞曲第一个可以获得舞伴的人
- 显然,先入队的男士或女士先出队配成舞伴,因此该问题具有典型的先进先出特性,可以用队列作为算法的数据结构
栈的表示和操作的实现
顺序栈的表示和实现
-
存储方式:同一般线性表的顺序存储结构完全相同。
利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址端。
- 附设top指针,知识栈顶元素在顺序表中的位置。
- 另设base指针,指示栈底元素在顺序栈中的位置。
- 另外,用stacksize表示栈可使用的最大容量
但是为了方便操作,通常top指针指示真正的栈顶元素之上的下标地址
-
空栈:base == top是栈空标志
-
栈满:top - base == stacksize
-
栈满时的处理方法:
- 报错,返回操作系统。
- 分配更大的空间,作为栈的存储空间,将原栈的内容移入新栈。
-
使用数组作为顺序栈存储方式的特点:
简单方便,但容易产生溢出(数组大小固定)
- 上溢(overflow):栈已经满,又要压入元素
- 下溢(underflow):栈已经空,还要弹出元素
上溢是一种错误,使问题的处理无法进行;而下溢一般认为是一种结束条件,即问题处理结束。
顺序栈的表示
#define MAXSIZE 100
typedef struct
{
SElemType *base;//栈底指针
SElemType *top;//栈顶指针
int stacksize;//栈的可用最大容量
}SqStack;
指针之差所得是两个指针之间的距离(差几个元素)
顺序栈的初始化
Status InitStack(SqStack &S)//构造一个空栈
{
S.base = new SElemType[MAXSIZE];
//或S.base = (SElemType*)malloc(MAXSIZE*sizeof(SElemType));
if(!S.base)//存储分配失败
{
exit(OVERFLOW);
}
S.top = S.base;//栈顶指针等于栈底指针
S。stacksize = MAXSIZE;
}
顺序栈是否为空
Status StackEmpty(SqStack S)//若栈为空,返回TRUE;否则返回FALSE
{
if(S.top == S.base)
{
return TRUE;
}
else
{
return FALSE;
}
}
求顺序栈的长度
int StackLength(SqStack S)
{
return S.top - S.base;
}
清空顺序栈
- top指针直接指向栈底
Status ClearStack(SqStack S)
{
if(S,base)
{
S.top = S.base;
}
return OK;
}
销毁顺序栈
Status Destroy