1、中国水利水电出版社,1,第3章 栈和队列,中国水利水电出版社,2,通常称,栈和队列是限定插入和删除只能在表的“端点”进行的线性表。,栈和队列是两种常用的数据类型,中国水利水电出版社,3,3.1 栈,栈是限定仅能在表尾一端进行插入、删除操作的线性表,(a1, a2, . , ai -1, ai , ai+1, , an ),插入,删除,3.1.1 栈的定义及基本运算 一、什么是栈,中国水利水电出版社,4,栈的示意图,出栈,进栈,栈的特点 后进先出 (LIFO表),第1个进栈的元素在栈底, 最后一个进栈的元素在栈顶, 第1个出栈的元素为栈顶元素, 最后一个出栈的元素为栈底元素,二、栈的图示,中国水
2、利水电出版社,5,(1)初始化操作 InitStack(,三、栈的基本操作(1),中国水利水电出版社,6,(5)进栈操作 Push(,三、栈的基本操作 (2),中国水利水电出版社,7,栈的顺序存储结构,也是利用一组连续的内存单元依次存放自栈底到栈顶的数据元素,栈顶元素的位置,由一个称为栈顶指针的变量指示 。,栈的顺序存贮结构也称为顺序栈。,一、栈的顺序存储结构及实现,3.1.2 栈的顺序表示与实现,与线性表类似,栈也可以用顺序结构或链式结构存储。,中国水利水电出版社,8,SqStack: 结构类型名; SqStack类型的变量是结构变量。 base: 称为栈底指针,始终指向栈底位置; top:
3、 称为栈顶指针,约定栈顶指针指向栈顶元素的下一个位置; Stacksize:用于存放当前已分配的栈的存储空间的大小;,#define STACK_INIT_SIZE 100 / 栈存储空间的初始分配量 #define STACKINCREMENT 10 / 栈存储空间的分配增量 typedef struct SElemType * base; / 栈空间基址便于判断栈是否为空SElemType * top / 栈顶指针 int stacksize; / 当前分配的栈空间大小/(以sizeof(SElemType)为单位) SqStack;,顺序栈的类型定义,中国水利水电出版社,9,空栈,A 进
4、栈,B C D E 进栈,栈满,E D C 出栈,顺序栈的操作示例P.58,中国水利水电出版社,10,顺序栈的图示,base top stacksize,S,中国水利水电出版社,11,Status InitStack (SqStack / InitStack_Sq,(1)初始化操作,顺序栈的基本操作的实现,中国水利水电出版社,12,Status DetroyStack ( SqStack / DetroyStack_Sq,(2) 销毁栈操作,中国水利水电出版社,13,Status ClearStack ( SqStack / ClearStack,(3)置空栈操作,中国水利水电出版社,14,S
5、tatus GetTop (SqStack S, SelemType / GetTop_Sq,(4) 取栈顶元素操作 GetTop,中国水利水电出版社,15,Status Push (SqStack / Push,(5) 进栈操作 Push,中国水利水电出版社,16,Status Pop (SqStack ,(6) 出栈操作 Pop,中国水利水电出版社,17,栈的链式存储结构, 也称链栈, 如右图所示:,3.1.3 栈的链式表示及实现,中国水利水电出版社,18,链式栈图示,说明:1. 链栈可用线性链表表示;2. 链栈的栈顶指针就是链表的头指针;3. 进栈操作(push)就是在该线性链表第一个元
6、素结点之前插入一个新结点.4. 出栈操作(pop)就是删除链表的第一个元素结点。,中国水利水电出版社,19,3.2 栈的应用举例,例1 数制转换我们学习过各种数制的转换问题。十-八进制转换十-二进制转换等。下面我们设计一程序,实现十-八进制数的自动转换。该程序功能为:对于输入的任意一个非负十进制数,显示输出与其等值的八进制数。,中国水利水电出版社,20,我们这里介绍的方法基于下列原理: N = (Ndiv8)10 8+N mod 8N:十进制数,div:整除运算,mod:求余运算; 例:(1348)10 = 283+582+08+4 = (2504)8 N N div 8 N mod 8134
7、8 168 4 168 21 021 2 52 0 2,中国水利水电出版社,21,由上述求解过程可以看出,在计算过程中是从低位到高位顺序产生八进制数的各个数位。而显示时按照我们的习惯是从高位在前,低位在后,即按从高位到低位的顺序输出。即后计算出的(高)位数先输出,具有后进先出的特点。因此可将计算过程中得到的八进制数顺序进栈,按出栈序列打印输出的数,就是对应的八进制数。,中国水利水电出版社,22,例如:(1348)10 = (2504)8 其运算过程如下: N N div 8 N mod 8 1348 168 4 168 21 0 21 2 5 2 0 2,计算顺序,输出顺序,中国水利水电出版社
8、,23,void conversion( ) / 输入任意一个非负十进制整数,输出与其等值的八进制数 InitStack(S); / 建空栈 scanf(“%d”, ,中国水利水电出版社,24,例2 括号匹配的检验 假设在表达式中()或( ) 等为正确的格式,( )或( )或 ( ) 均为不正确的格式。,则 检验括号是否匹配的方法可用“期待的急迫程度”这个概念来描述。,中国水利水电出版社,25,分析可能出现的不匹配的情况:,到来的右括弧非所“期待”的;,例如:考虑下列括号序列: ( ) 1 2 3 4 5 6 7 8,到来的是“不速之客”;,直到结束,也没有到来所“期待”的括弧;,中国水利水电
9、出版社,26,算法的设计思想:,(1)凡出现左括弧,则进栈;,(2)凡出现右括弧,首先检查栈是否空若栈空,则表明该“右括弧”多余error否则和栈顶元素比较,若相匹配,则“左括弧出栈”否则表明不匹配error,(3)表达式检验结束时,若栈空,则表明表达式中匹配正确否则表明“左括弧”有余error,中国水利水电出版社,27,Status matchpairs(SElemType str) SqStack *S; int tag=1; char *ch,e; / e存放出栈字符/ tag=1表示表达式中括号正确配对,tag=0表示不配对InitStack(,中国水利水电出版社,28,/遇到), 或
10、,弹出栈顶字符case ): Pop(S, ,中国水利水电出版社,29,例3 行编辑程序问题,如何实现?,是否每接受一个字符即存入存储器 ?,中国水利水电出版社,30,设立一个输入缓冲区,用以接受用户输入的一行字符,然后逐行存入用户数据区; 并假设“#”为退格符,“”为退行符。,在用户输入一行的过程中,允许用户输入出差错,并在发现有误时可以及时更正。,合理的作法是:,中国水利水电出版社,31,假设从终端接受了这样两行字符:whli#ilr#e(s#*s)outputchar(*s=#+);,则实际有效的是下列两行:while (*s)putchar(*s+);,中国水利水电出版社,32, wh
11、ile (ch != EOF / 从终端接收下一个字符,ClearStack(S); / 重置S为空栈if (ch != EOF) ch = getchar(); ,while (ch != EOF) / EOF为全文结束符,将从栈底到栈顶的字符传送至调用过程的数据区;,中国水利水电出版社,33,(1) 问题的提出设计一个小计算器(程序),希望从键盘上输入一个算术表达式(由运算符操作数构成的字符串)后,在屏幕上显示表达式的求值结果。,例4 表达式求值,中国水利水电出版社,34,(2) 表达式的构成 操作数+运算符+界符(如括号) (3) 表达式的求值:例 5+6(1+2)-4按照四则运算法则,
12、上述表达式的计算过程为:5+6(1+2)-4=5+63-4 = 5+18-4= 23-4=19 表达式中运算符的运算顺序为: + , , +, -如何确定运算符的运算顺序?,1,2,3,4,1,2,3,4,中国水利水电出版社,35,+,2,1,-,*,/,(,),#,+ - * / ( ) #, , , , , =, , =,(4) 算符间的优先关系表,注: 1 ,2 是相邻算符, 1在左, 2在右12 表示1的优先权低于2 P.52,中国水利水电出版社,36,(5) 算符优先算法分析表达式 5+4(1+2)-6 计算过程:,从左向右扫描表达式:遇操作数 进栈;遇运算符号 j与前面的刚扫描过的
13、运算符 i 比较 若 i j 则说明 i 是已扫描算符中优先级最高者, j 出栈,进行运算; 若 i = j 若 i 为(,j 为 ), j 出栈,说明括号内的式子已计算完;若 i 为 #,出栈,栈空,表达式计算完毕。,5+4 (1+2)- 6,进行运算的算符i是当前扫描过的运算符中优先级最高者,可以利用两个栈分别保存扫描过程中遇到的操作数和运算符。,后面也许有优先级 更大的算符,中国水利水电出版社,37,建立两个工作栈。OPTR栈保存运算符;OPND栈-保存操作数或运算结果。算法3.3 operandType EvaluateExpression( ) / 算术表达式求值的算符优先算法。设O
14、PTR和OPND/ 分别为运算符栈和运算数栈,OP为运算符集合。InitStack(OPTR); Push (OPTR, #);InitStack(OPND); c=getchar( );While(c!= # | GetTop(OPTR)!=#) if (! In (c, OP) / In(c, OP)是判断c 是否为运算符的函数 Push(OPND, c); c=getchar( ); / 不是运算符则进数据栈OPNDelse,中国水利水电出版社,38,续 switch (Precede(GetTop(OPTR), c) case : / 栈顶算符优先权高, 出栈运算,并将运算结果入栈OP
15、NDPop(OPTR, theta);Pop(OPND, b); Pop(OPND, a);Push(OPND, Operate(a, theta, b);break; / switch/whilereturn GetTop(OPND);/EvaluateExpression,中国水利水电出版社,39,3.3 栈与递归,1什么是递归递归是一个很有用的工具,在数学和程序设计等许 多领域中都用到了递归。递归概念:一个用自己定义自己的定义,称为递归定义。例 n!= 1*2*3*4*(n-1)* n n!递归定义 n!= 1 当 n=0 时n!= n(n-1)! 当 n0 时,用(n-1)! 定义n!
16、,中国水利水电出版社,40,例1 编写求解 阶乘n! 的递归算法n! 的递归定义:基本项: n!=1 当 n=1递归项: n!=n (n-1)! 当 n 1递归算法:int fact (int n) / 算法功能是求解并返回n的阶乘 If(n=1) return(1);else return(n*fact (n-1));,中国水利水电出版社,41,例2 编写求解Hanoi塔问题的递归算法有3个各为X,Y,Z 的塔座,在 X 项上有 n 个大小不同,依小到大编号为1,2,n的圆盘。 现要求将 X 上的 n 个圆盘移至 Z 上,并仍以同样顺序叠放, 圆盘移动时必须遵守下列原则:(1)每次移动 1
17、个盘子;(2)圆盘可以放在 X,Y,Z 中的任一塔座上;(3)任何时刻都不能将较大的圆盘压放在较小圆盘之上;,中国水利水电出版社,42,例: n=3 时圆盘移动的过程如下图所示:,Y,X,Z,中国水利水电出版社,43,求解Hanoi塔问题的递归描述(定义)基本项: n=1时,将 n 号圆盘从 X 移至 Z;递归项: n1时,将 X 上 1 n-1号圆盘移至Y;将 X 上的 n 号圆盘从 X 移至 Z; 将 Y 上 1 n-1号圆盘从 Y 移至 Z;,将规模为n的问题的求解归结为规模为n-1的问题的求解,有了问题的递归定义, 很容易写出对应的递归算法:,中国水利水电出版社,44,void han
18、oi (int n, char x, char y, char z) / 将塔座x上按直径由小到大,且自上而下编号为1至n的n个圆盘按规则搬到 / 塔座z上,y可用作辅助塔座。 / 搬动操作move(x, n, z) 可定义为(c是初值为0的全局变量,对搬动计数): / printf(“%d. Move disk %d from %c to %c n”, +c, n, x, z); if (n= =1) move(x,1,z); /将编号为1的圆盘从x移动z else hanoi(n-1, x, z, y); / 将x 上编号为 1 至 n-1 的圆盘移到 y, z 作辅助塔 move(x,
19、n, z); / 将编号为 n 的圆盘从x 移到 z hanoi(n-1, y, x, z); / 将y 上编号为 1 至 n-1 的圆盘移到 z, x 作辅助塔 算法3.5 Hanoi塔问题,中国水利水电出版社,45,3.4 队 列,3.4.1 队列的定义及基本运算 一 什么是队列,队列是限定仅能在表头进行删除,表尾进行插入的线性表。,中国水利水电出版社,46,说明:设(a1, a2 , a3 , . , an )为一队列 (1) 表尾称作队尾,表头称为队头; (2) a1 为队头元素,an 为队尾元素; (3) 在表尾插入元素操作,称为入队操作;在表头删除元素的操作,称为出队操作; (4)
20、 元素按 a1,a2, a3, . an 顺序入队,第1个入队的元素为 a1 ,最后一个入队的元素是an ;第1个出队的元素为 a1 , 最后一个出队的元素是an (5) 队列具有先进先出的特点,所以又称为先进先出表(FIFO表),中国水利水电出版社,47,二 队列的基本操作(1)初始化操作InitQueue( &Q) 功能:构造一个空队列Q;(2)销毁操作 DestroyQueue( &Q) 功能:销毁已存在队列Q; (3)置空操作 ClearQueue(&Q) 功能:将队列Q置为空队列; (4)判空操作 QueueEmpty(Q) 功能:若队列Q为空,则返回True,否则返回False;,
21、中国水利水电出版社,48,(5) 取队头元素操作 GetHead(Q,&e) 功能:取队头元素,并用 e 返回; (6) 入队操作 EnQueue( &Q, e ) 功能:将元素 e 插入 Q 的队尾;(7)出队操作 DeQueue( &Q, &e) 功能:若队列不空,则删除 Q 的队头元素,用 e 返回其值,并返回OK,否则返回ERROR;,中国水利水电出版社,49,3.4.2 队列的顺序表示和实现,一、 队列的顺序存贮结构 队列的顺序存储结构是用一组地址连续的存储单元依次存放队列中的各个元素,并用指针front指向队头,指针rear指向队尾 。,实际上,front指针始终指向队头元素,re
22、ar指针实际上并不是指向队尾元素,而是指向队尾元素的下一个位置。如图:,队列的顺序存贮结构图示,中国水利水电出版社,50,#define MAXSIZE 100 / 最大队列长度 typedef struct QElemType *base; / 初始化时动态分配存储空间的基址 int front; / 队头指针,指向队头元素 int rear; / 队尾指针,指向队尾元素的下一个位置 SqQueue;,二、顺序队列的类型定义,中国水利水电出版社,51,顺序队列常常会出现“假溢出”现象,如图所示。虽然队列中还有一定的存储空间,但因为front指针与rear指针均向同一个方向移动,导致该队列不能
23、再进行入队操作。,中国水利水电出版社,52,为了避免“假溢出”,可以这样规定,一旦 rear指针(或 front指针)指向了存储空间的末尾位置,如果此时再进行入队(或出队)操作,则将相应的指针平移到数组的起始位置,即是将队列假想为一个循环的环状空间,我们称之为循环队列。,中国水利水电出版社,53,三、循环队列操作图示,front,rear,空队 front = rear 是队空标志,3,1,2,0,MAXSIZE = 4,中国水利水电出版社,54,front,rear,空队 front = rear 是队空标志,B,C,rear,front,3,1,2,0,MAXSIZE = 4,A 出队,三
24、、循环队列操作图示,队列中含有3个元素,rear已经指向存储空间的末端,不能再向上移动,中国水利水电出版社,55,front,rear,空队 front = rear 是队空标志,C,rear,front,rear,front,3,1,2,0,MAXSIZE = 4,B,C,B 出队,三、循环队列操作图示,队列中含有2个元素,中国水利水电出版社,56,MAXSIZE = 4,此刻rear已经指向存储空间的末端 若再有元素进队列,则执行语句: rear=(rear+1)% MAXSIZE,三、循环队列操作图示,D元素进队列,执行: rear=(rear+1)% MAXSIZE 经过计算,rear
25、的值为0,中国水利水电出版社,57,front,rear,空队 front = rear 是队空标志,C,C,rear,front,rear,front,D,E,3,1,2,0,MAXSIZE = 4,D,E 进队,三、循环队列操作图示,Rear重新指向存储空间的起始位置,此刻队列仍有存储空间,可以进行进队操作,中国水利水电出版社,58,front,rear,空队 front = rear 是队空标志,C,C,rear,front,D,E,F,3,1,2,0,MAXSIZE = 4,F 进队 ,队满队满与队空条件混淆,E,D,三、循环队列操作图示,中国水利水电出版社,59,front,rear
26、,空队 front = rear 是队空标志,C,rear,front,D,E,F,3,1,2,0,MAXSIZE = 4,为了区分队满与队空,规定队满条件为: ( rear + 1) % MAXQSIZE = front 所以队列中最多能包含的元素个数比实际空间少一个,队满,三、循环队列操作图示,中国水利水电出版社,60,出队时应先判 - 队是否空?队空条件: Q.rear = Q.front 不空,则出队。进队时应先判 - 队是否满?队满条件: ( ( Q.rear + 1) % MAXQSIZE ) = Q.front不满,则进队 。,中国水利水电出版社,61,(1) 初始化循环队,St
27、atus InitQueue (SqQueue ,四、循环队列基本操作的实现,Q.base = new QElemType MAXSIXE;,中国水利水电出版社,62,(2)进队操作,Status EnSqQueue (SqQueue ,再进队 满(上溢),再进队循环、满,再进队循环、不满,S 进队,中国水利水电出版社,63,(3)出队操作P.76,Status DeSqQueue (SqQueue ,队空,中国水利水电出版社,64,3.4.3 链队列 队列的链式存储结构和实现,一、定义 用链表表示的队列,称为链队列,空链队列,非空链队列,中国水利水电出版社,65,二、链队列的类型定义,typ
28、edef struct QNode / 链队列结点的类型定义 QElemType data; struct QNode *next; QNode , *QueuePtr;typedef struct / 链队列表头结点的类型定义 QueuePtr *front; / 队头指针,指向链表的头结点 QueuePtr *rear; / 队尾指针,指向队尾结点 LinkQueue;,中国水利水电出版社,66,设Q为LinkQueue类型的变量,Q为链队列的表头结点,用于 存储队列队头指针和队尾指针。,链队列的 表头结点,链队列的 头结点,空链队列,非空链队列,中国水利水电出版社,67,(1)初始化队操
29、作:InitQueue ( LinkQueue ,三、 链队列基本操作的算法,空链队列,中国水利水电出版社,68,销毁队操作: DestroyQueue( LinkQueue ,中国水利水电出版社,69,e 入队前,e 入队后,(3)入队操作EnQueue(LinkQueue &Q, QElemType e),入队操作图示:,中国水利水电出版社,70,Status EnQueue(LinkQueue ,e 入队,中国水利水电出版社,71,(4)出队操作:Status DeQueue(LinkQueue ,中国水利水电出版社,72,3.4.4 队列的应用举例队列与栈一样,也是程序设计中经常使用的
30、数据结构,其应用主要体现在以下4个方面:,(1)解决计算机主机与外设不匹配的问题 (2)解决由于多用户引起的资源竞争问题,(3)离散事件的模拟-模拟实际应用中的各种排队现象 (4)用于处理程序中具有先进先出特征的过程,操作系统课程,中国水利水电出版社,73,第1行 1 第2行 1 1 第3行 1 2 1 第4行 1 3 3 1 第5行 1 4 6 4 1,例1.二项式系数值(杨辉三角),可以看出第i行除首尾元素之外,中间i-2个元素的值均可通过上一行的元素计算出来,中国水利水电出版社,74,利用循环队列打印杨辉三角的过程:,设队列的最大容量为5,*表示已经出队列。,1,1,1,q.front,q.rear,1,1,1,*1,q.front,q.rear,*1,1,1,*1,2,q.front,q.rear,*1,*1,1,1,2,q.front,q.rear,1,*1,1,1,2,q.front,q.rear,1,3,*1,1,2,q.front,q.rear,中国水利水电出版社,75,