【数据结构】第三章 栈和队列 堆栈 队列

知识点概述

后进先出(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个尾指针亦应用一个二维数组表示。其数据类型定义如下。

总结:

栈是一种只允许在线性表的一端进行插入和删除操作的线性表。与线性表类似,栈也有两种存储方式:顺序存储和链式存储。采用顺序存储结构的栈称为顺序栈,采用链式存储结构的栈称为链栈。递归的调用过程也是系统借助栈的特性实现的。因此,可利用栈模拟递归调用过程,从而消除递归。队列是只允许在表的一端进行插入操作,在另一端进行删除操作的线性表。队列有两种存储方式:顺序存储和链式存储。顺序队列存在“假溢出”的问题,为了避免“假溢出”,可用循环队列表示顺序队列。为了区分循环队列的队空还是队满,有两种方案:设置一个标志位和少用一个存储单元。


资料仅供学习使用

编者能力有限,如有错误欢迎留言交流

编者的其他专栏:

C语言

单片机原理

模式识别原理

数字电子技术

自动控制原理

模拟电子技术

数据结构

线性代数

复变函数与积分变换

 概率论与数理统计

高等数学

大学物理

电路原理

关注编者了解更多

  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值