栈
知识点概述
后进先出(Last In First Out,LIFO)的线性序列,称为“栈”。栈也是一种线性表,只不过它是操作受限的线性表,只能在一端进出操作。进出的一端称为栈顶(top),另一端称为栈底(base)。栈可以用顺序存储,也可以用链式存储,分别称为顺序栈和链栈。
3.1 堆栈
3.1.1 堆栈的定义及基本运算
堆栈简称为栈,是限定只能在表的一端进行插入和删除操作的线性表。在表中,允许插入和删除的一端称作“栈顶”,另一端称作“栈底”。通常将元素插入栈顶的操称作为“入栈”(进栈或压栈),称删除栈顶元素的操作为“出栈”)表.
顺序栈
栈顶位置top的两种定义方式
我们采用(b)的方式,top初值-1,当有数据入栈,top为栈顶数据的下标(数组下标从0开始)
•则栈1的栈顶表示为:s->top1,栈2的栈顶表示为:s->top2;•栈1的进栈操作使得栈顶1右(后)移,即s->top1++,栈2的进栈操作使得栈顶2左(前)移,即s->top2--;•栈满时两个栈顶相邻,即s->top1+1==s->top2
将元素e压入栈
出栈操作
3.1.3 栈的链式存储结构
链栈的类型描述如下:
链栈
以链表形式存储的栈
入栈操作
出栈操作
链栈操作的相关代码
【例2】读算法,给出算法的返回值与参数n(自然数)的关系
或者可以调用链栈的基本运算实现:
3.2 栈典型题例
【例1】若某堆栈的输入序列为1,2,3,...,n-1,n,输出序列的第1个元素为n,问第i个输出元素是什么?
【分析与解答】
堆栈的输入序列为1,2,3,...,n-1,n,堆栈的特性是后进先出,输出序列的第1个元素为n,那么第2个元素为n-1,第3个元素为n-2,...,第i个输出元素是n-i+1。
【例2】简述以下算法的功能。
【分析与解答】(1) 算法Reverse中,堆栈s的元素从栈底到栈顶的序列设为e1,e2,...,en,while循环将s的元素出栈,存入a数组,a数组中各元素的值如表所示,for循环将a数组元素依a[0],a[1],...,a[n-1](即en,en-1,..., e1)的顺序入s栈,从栈底到栈顶的序列为en,en-1,..., e1。因此,该算法借助数组a实现了堆栈s的逆置。
(2)算法Remove中,第1个while循环将s的元素(除元素e)出栈进入t栈,t栈是s栈不包括e元素的逆置,第2个while循环则将t的元素出栈后又进入s栈,s栈是t栈的逆置,最终s经过两次逆置又恢复成原来的元素序列(不包括元素e),因此该算法借助堆栈t将堆栈s中的数据元素e删除。
【例3】下图是一组n条带头结点的链表表示的堆栈(n个链栈),top[0],top[1],...,top[n-1]分别是各堆栈栈顶指针,试给出该组堆栈的数据类型定义,并写一算法对这组堆栈初始化。
【分析与解答】数据类型(链表中结点类型StackNode与链指针类型LinkStack)定义如下:
顺序栈与链式栈的比较
•实现顺序栈和链式栈的所有操作都只需要常数时间,因此栈的两种实现方式的优劣仅体现在它们的存储效率上
•顺序栈需要预先申请一个固定长度的一维数组,并自始至终全部占用,当栈中元素个数相对较少时,空间浪费较大
•链式栈的长度虽然可变,但是每个元素都需要一个指针域,这又产生了结构性空间开销
栈的应用
过程的嵌套调用(栈式管理)
递归过程及其实现
⚫递归:函数直接或间接的调用自身叫~
⚫实现:建立递归工作栈
例递归的执行情况分析
递归调用执行情况如下:
❖回文游戏:顺读与逆读字符串一样(不含空格)
1.读入字符串
2.去掉空格(原串)\
3.压入栈
4.原串字符与出栈字符依次比较
若不等,非回文若直到栈空都相等,回文
算法描述如下:
检查括号匹配:表达式[a + (d + e)]时栈的变化过程
【例】背包问题:
假设有n件体积分别为w1,w2,...wn的物品和一个能装载体积为T的背包.能否从n件物品中选择若干件恰好装满背包, 即wi1+wi2+...+wik=T,则背包问题有解;否则无解.
以W(1,8,4,3,5,2), T=10为例(1,4,3,2),(1,4,5), (8,2)和(3,5,2)是其解
从背包中取出物品再继续搜索的策略称之为回溯,其规则是“后进先出”,即背包以栈的操作完成求解。
算法实现:
地图四染色问题:
队列
队列
限定操作的线性表;
队列的定义及运算
•队列简称为队,是限定只能在表的一端作插入运算、在另一端作删除运算的线性表;
•在表中,允许插入的一端称作“队尾”,允许删除的另一端称作“队首”(或“队头”);
•通常将元素插入队尾的操作称作为入队列(或入队),称删除队首元素的操作为出队列(或出队)。
顺序队列的知识点和动态分配
队列的基本运算如下:
(1)InitQueue(),初始化队列。
(2)QueueEmpty(q),判定队列q是否为空。
(3)QueueLength(q),求队列q的长度。
(4)GetHead(q),获取队列q队首元素的值。
(5)AddQueue(q,e),将元素e入队。
(6)DeleteQueue(q),删除队首元素(队首元素出队列)。
队列的顺序存储结构
顺序队列
顺序队列front指向当前队首,rear指向当前队尾下一位置
问题:当front==0,rear==MaxSize,再有元素入队发生溢出——真溢出
当front != 0,rear==MaxSize,再有元素入队发生溢出——假溢出
循环队列:
•入队的新元素放置到所有已有元素的后面
•经过若干次入队、出队操作后,含n个元素的队列的示意图如下所示,其中深色部分表示队列中的元素实际占用的数组单元
为了能充分地利用数组空间,避免出现假溢出,将顺序队列定义为循环队列:
#define MaxSize 队列可能达到的最大长度
typedef struct
{ ElementType elem[MaxSize];
int front, rear; /*队首、队尾指示器*/
} CirQueue
为了对循环队列空、满加以区分,始终让front指向队首元素,rear指向当前队尾下一位置(rear所指位置不存放队尾元素,损失一个存储单元空间)。
初始化时:q.front=q.rear=0
循环队列为空的条件是:q.front==q.rear
循环队列为满的条件是:q.front==(q.rear+1)%MaxSize
链队
链式存储的队列表示
链队列的描述
初始化队列
判断队列为空
获取队列队首值
将元素e入队
出队列
3.4队列典型例题
【例1】链队列q中存放有一批整数,写一算法,试将该队列中的正整数、零及负整数分别存放在两条不同的链队列q1、q2中,并且q1、q2中的值要求保持原来的相对顺序
答:链队列q1、q2中的值来源于q队列,并且其中的值要保持原来的相对顺序,那么就需要对q连续地执行出队列操作,并每次对q中出队列的元素x进行判断:若x>0,进q1;若x<=0,进q2。直到q为空。
【例2】已知循环队列存储空间为数组a[21],且当前队列的头指针和尾指针的值分别为8和3,则该队列的当前长度是多少?
【分析与解答】循环队列长度=(q.rear+ MaxSize–q.front) % MaxSize=3+21-8=16。亦即当前队列元素位于:a[8], a[9], a[10] ,a[11], ...,a[19], a[20], a[0], a[1], a[2]
【例3】简述下列算法的功能。
【分析与解答】第1个while循环将q队列的全部元素出队,出队后进入堆栈s。第2个while循环则将s的全部元素出栈后又进入q队列,q中存放的是原来元素的逆序。所以算法借用堆栈s实现了q队列的逆置。
【例4】假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾元素结点,试编写入队列算法。
【分析与解答】下图是仅有队尾指针的循环链队列,入队算法如下。
【例5】如果用一个数组q[0..m-1]表示循环队列,该队列只有一个队列头指针front,不设队列尾指针rear,而改设计数器count用以记录队列中元素的个数。
(1)编写入队算法;
(2)此种队列能容纳元素的最大个数是多少?
【分析与解答】用此种方法表示队列时,为空的条件是count=0,为满的条件是count=m,入队算法如下。
Void AddQueue(CirQueue *q, ElementType e)
{ if (count == m) /*队满*/
printf(“Full”);
else
{ q−>elem[(q−>front+count)% m]=e ;
count++;}
} /* AddQueue */此种队列能容纳元素的最大个数是m。
【例6】n条循环队列构成一组,用一个数组存放该队列组中每条队列的头指针与尾指针,试给出该组队列的数据类型定义,若队列编号分别为:0,1,2,...,n-1,试写算法:
①求i队列的长度;
②i队列首元素出队列。
【分析与解答】一条循环队列中的元素是用一个一维数组存放的,一组(n条)循环队列的元素则可用一个二维数组存放。一条队列对应头、尾两个指针,一组(n条)队列的n个头指针与n个尾指针亦应用一个二维数组表示。其数据类型定义如下。
总结:
栈是一种只允许在线性表的一端进行插入和删除操作的线性表。与线性表类似,栈也有两种存储方式:顺序存储和链式存储。采用顺序存储结构的栈称为顺序栈,采用链式存储结构的栈称为链栈。递归的调用过程也是系统借助栈的特性实现的。因此,可利用栈模拟递归调用过程,从而消除递归。队列是只允许在表的一端进行插入操作,在另一端进行删除操作的线性表。队列有两种存储方式:顺序存储和链式存储。顺序队列存在“假溢出”的问题,为了避免“假溢出”,可用循环队列表示顺序队列。为了区分循环队列的队空还是队满,有两种方案:设置一个标志位和少用一个存储单元。
资料仅供学习使用
编者能力有限,如有错误欢迎留言交流
编者的其他专栏:
关注编者了解更多