第三章 栈与队列
This presentation is the property of its rightful owner.
Sponsored Links
1 / 62

第三章 栈与队列 PowerPoint PPT Presentation


  • 102 Views
  • Uploaded on
  • Presentation posted in: General

第三章 栈与队列. 两种重要的,限定操作的线性结构。. 学习要点:. 栈的基本概念、结构特征和操作要点。 栈在顺序存储和链式存储结构下各种操作的实现,栈满与栈空判定条件。 队列基本概念、结构特征和操作要点。 队列在两种存储结构下各种操作如何实现,队满与队空判定条件。 循环队列基本概念,循环队列的队空与队满判定条件。 栈和队列的基本应用和应用要点。. §3.1 栈. 3.1.1 栈的基本概念. 栈的例子 :操作系统中中断现场的保留等。 定义: 限定只能在 表的一端 进行插入或删除操作的线性表。 修改原则 : 后进先出 ( LIFO ). 练习:.

Download Presentation

第三章 栈与队列

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


5267774

第三章 栈与队列

两种重要的,限定操作的线性结构。


5267774

学习要点:

  • 栈的基本概念、结构特征和操作要点。

  • 栈在顺序存储和链式存储结构下各种操作的实现,栈满与栈空判定条件。

  • 队列基本概念、结构特征和操作要点。

  • 队列在两种存储结构下各种操作如何实现,队满与队空判定条件。

  • 循环队列基本概念,循环队列的队空与队满判定条件。

  • 栈和队列的基本应用和应用要点。


5267774

§3.1 栈

3.1.1 栈的基本概念

栈的例子:操作系统中中断现场的保留等。

定义:限定只能在表的一端进行插入或删除操作的线性表。

修改原则:后进先出(LIFO)


5267774

练习:

  • 设进栈顺序为1、2、3、4,请说出以下的出栈顺序是否可能?

    A、1 2 3 4 B、4 3 1 2

    C、2 1 4 3 D、3 2 1 4

    E、3 4 2 1 F、3 4 1 2

×

×


5267774

栈的抽象数据类型:

  • ADT Stack is

  • {数据对象:D={ai|ai∈DataType,i=0,1,2,…,n-1,n≥0}

  • 数据关系:R={<ai-1,ai>|ai-1,ai∈D,i=1,2,…,n-1}约定an-1为栈顶,a0为栈底。

  • 基本操作:

  • (1)Stack InitStack() 初始化并返回一个空栈;

  • (2)ClearStack (Stack S) 清空栈S中的元素;

  • (3)int IsEmpty(Stack S) 判断栈是否为空,返回栈空或非空标志;

  • (4)int IsFull(Stack S) 判断栈是否满,返回栈满或不满标志;

  • (5)Stack Push(Stack S, DataType x)

    若栈未满,将数据元素x入栈;

  • (6)Stack Pop(Stack S) 若栈非空,删除栈顶元素;

  • (7)DataType GetTop(Stack S)

    若栈非空,返回栈S中的取栈顶元素;

  • } ADT Stack


3 1 2

3.1.2 栈的顺序存储结构

  • 数组定义栈:stack[max]

  • 规定:top指向栈顶元素所在位置

  • 初始:top=-1(空栈)

    空栈:top=-1,出栈下溢

    栈满:top=max-1,进栈上溢

max-1

1

0


3 1 2 2

3.1.2 栈的顺序存储结构2

(a)空栈 (b)栈中有两个元素 (c)栈满

图3-2 顺序栈各种情况


3 1 2 3

3.1.2 栈的顺序存储结构3

用C语言定义栈的顺序存储结构如下:

#define MAXSIZE 100

/* MAXSIZE指的是栈的最大长度 */

typedef struct

{DataType elem[MAXSIZE];

/* 定义数组依次存放栈里的数据元素*/

int top; /* 指向栈顶元素的下标 */

}Stack;


5267774

基本操作:顺序栈初始化

算法要求返回一个空的栈,空栈中数据元素个数为0,top值为-1。

00 Stack InitStack()

01 {

02 Stack S;

03 S.top=-1; /* 空栈的top值为-1 */

04 return(S);

05 }


5267774

已知栈S及数据元素x,需要将x放置进栈S

基本操作:进栈

00 Stack Push(Stack S, DataType x)

/* 若栈未满,将数据元素x入栈 */

01 {

02 if (S.top==MAXSIZE-1)

03 printf(“栈已满,无法入栈!”);

04 else

05 {

06 S.top=S.top+1;

07 S.elem[S.top]=x;

08 }

09 return(S);

10 }


5267774

基本操作:出栈

00 Stack Pop(Stack S) /* 若栈非空,删除栈顶元素 */

01 {

02 if (S.top== -1)

03 printf(“栈是空的,无法出栈!”);

04 else

05 S.top=S.top-1;

06 return(S);

07 }


5267774

已知栈S,取出栈顶元素

基本操作:取栈顶元素

00 DataType GetTop(Stack S)

/* 若栈非空,取栈顶元素赋值给x */

01 {

02 if (S.top== -1)

03 printf(“栈是空的,无法取栈顶!”);

04 else

05 x=S.elem[S.top];

06 return(x);

07 }


5267774

原理:利用栈只能在栈顶端进行操作的特性,将两个栈的栈底分别设在数组的头和尾,两个栈的栈顶在数组中动态变化,栈1元素比较多时就占用比较多的存储单元,元素少时就让出存储单元供栈2可用,提高了数组的利用率。

多栈共享技术:双端栈

图3-3 双端栈


3 1 3

3.1.3 栈的链式存储结构

规定:top指向栈顶元素地址

链栈中结点结构和单链表一样,指针域指向次顶元素。

用C语言可定义如下:

00 struct node

01 {

02 DataType data;

03 struct node *next;

04 };

05 typedef struct node StackNode;

06 StackNode *top;

设指针域指向次顶结点


3 1 4

3.1.4 栈的链式存储结构

图3-5 链栈


5267774

基本操作:链栈初始化

初始化链栈即建立一个空的链栈,只有top结点,其指针域为空。

00 StackNode *InitStack()

01 {

02 StackNode *top;

03 top=( StackNode *) malloc (sizeof(StackNode));

04 top->next=NULL;

05 return(top);

06 }


5267774

基本操作:进栈

已知一个数据元素x以及链栈top,要求完成x进栈操作。


5267774

基本操作:进栈2

00 StackNode * Push(StackNode *top,DataType x)

01 {

02 StackNode *p;

03 p=( StackNode *) malloc (sizeof(StackNode)); /* 申请结点p */

04 p->data=x; /* x存储到新结点p中 */

05 p->next=top->next; /* p指向栈顶结点 */

06 top->next=p; /* top结点指向p,p成为新的栈顶 */

07 return(top);

08 }


5267774

基本操作:出栈


5267774

基本操作:出栈2

00 StackNode * Pop(StackNode *top)

01 {

02 StackNode *p;

03 if (top->next==NULL) /* 判断栈是否为空 */

04 {

05 printf(“栈空,无法出栈!”);

06 return(top);

07 }

08 p=top->next; /* p指向栈顶,待删除 */

09 top->next=p->next; /* top结点指针域跳过p,指向p的后继 */

10 free(p); /* 释放p占用的空间 */

11 return(top);

12 }


3 1 41

3.1.4 栈的应用

1.栈在递归程序中的应用——阶乘运算

递归问题的解决可以大致做这样的算法描述:

if (递归结束条件)

return (递归结束条件下的返回值);

else

return (递归计算公式);


3 1 42

3.1.4 栈的应用

1.栈在递归程序中的应用——阶乘运算

解释:

●递归计算公式:是大问题在变成次大问题中的表现出来的规律,要表达清楚这个大问题与下一级的次大问题有什么联系。

●递归结束条件:解决递归问题中的分解不能无终止地分解下去,需有一个结束的条件。这样才可以由结束递归再返回层层解套,最终解决整个问题。递归的结束条件也称为递归出口。


3 1 43

1 (n=0,1)

n!=

n*(n-1)! (n>1)

3.1.4 栈的应用

例子:n的阶乘的计算


3 1 44

3.1.4 栈的应用

例子:n的阶乘的计算

00 int fact (int n)

01 {

02 int fac;

03 if (n==0)

04 fac=1;

05 else

06   fac=fact (n-1)*n; 

07 return fac;

08 }


3 1 4 2

3.1.4 栈的应用2

2.栈在递归程序中的应用——Hanoi塔问题

解释Hanoi游戏:

(1)设有三根杆子A,B,C。A杆上有n个盘子,从小到大相叠着,编号1—n;

(2)每次移动一块盘子到另外一个杆上,但要求每个杆上的盘子只能是小的叠在大的上面;

(3)把所有盘子从A杆全部移到C杆上,可利用B杆作为过渡。


3 hanoi

3阶Hanoi问题求解过程:


3 1 4 21

3.1.4 栈的应用2

n阶Hanoi递归算法:

00 void Hannoi(int n,char A,char B,char C)

01 {

02 if(n==1)

03 Move(1,A,C); /* 将1号盘从A杆移到C杆 */

04 else

05 {

06 Hannoi(n-1,A,C,B); /* 将A杆上n-1个盘借助C杆移动到B杆 */

07 Move(n,A,C); /* 将n号盘从A杆移到C杆 */

08 Hannoi(n-1,B,A,C); /* 将B杆上n-1个盘借助A杆移动到C杆 */

09 }

10 }


3 1 4 3

3.1.4 栈的应用3

n阶Hanoi非递归算法见3.1.4节算法3-11。


3 1 4 31

3.1.4 栈的应用3

3.栈在括号匹配中的应用

问题理解:

假设一个算术表达式中包含圆括号、方括号和花括号三种类型的括号,编写一个算法,判别表达式中括号是否正确配对。

如“fha{bh(gf[hg]gt)q}gfr”是正确匹配的,“fds[fsd(gfd]g”则缺少“)”。


3 1 4 32

3.1.4 栈的应用3

3.栈在括号匹配中的应用

程序见3.1.4节算法3-12。

程序设计要点:

顺序扫描被判别的表达式,每当遇到"("、"["或"{"时,将其压入栈中(算法3-12第11-14行);当遇到")"、"]"或"}"时,先检查栈是否是空的,如果是空的,表示缺少对应的左边括号,接着检查当前栈顶元素是否是对应的"("、"["或"{",若是则退栈,否则返回表示不匹配。如对")"的处理(程序中第15-28)。当整个算术表达式检查完毕时,还要对栈的情况进行判断,如栈是空的说明所有左右括号相匹配,否则缺少栈内左括号相对应的右括号(程序中第56-59行)。


3 1 4 4

θ1θ2

+

-

*

+

>

<

>

-

>

>

<

>

\

>

>

>

>

<

<

<

=

3.1.4 栈的应用4

4.栈在表达式求值中的应用

(1)由计算规则得算符优先关系表

3+5*3 3+5+5

θ1:先来的算符 θ2:后来的算符

>


5267774

4.栈在表达式求值中的应用2

(2)原理

两工作栈:一个算符栈,一个操作数栈

a.读取到操作数,直接入操作数栈

b.读取到运算符,取运算符栈栈顶元素与该算符比较优先级:

“<”:读取符号入运算符栈;

“>”:运算符栈出一元素,操作数栈出两元素,

运算后,结果放回操作数栈;

“=”:运算符栈出一元素,与之相抵消。


5267774

4.栈在表达式求值中的应用3

计算#3*(15-5)/2#栈的变化过程


5267774

4.栈在表达式求值中的应用2

(2)原理

算法结束标志:运算符栈为空,操作数栈有一元素,即为结果。

表达式求值算法见3.1.4节算法3-13。


5267774

上机(选做):

  • 输入十进制数,转换成R进制数。

  • 输入一包含“(”和“)”的字符串,检测括号是否匹配(其中括号中能嵌套括号),并输出括号是否匹配的信息(匹配、缺少左括号、缺少右括号);


5267774

分析:

  • gets(fun)

  • while (fun[i]!=’\0’)

  • {if (fun[i]==’(‘):入栈;

  • if (fun[i]==‘)’):

  • if 栈空:错,缺左边,return;

  • else 出栈;

  • i=i+1;

  • }

  • 如栈空 对!!!

  • 否则 错,缺少右边


5267774

§3.2 队列

3.2.1 队列基本概念

  • 队列的例子:排队等。

    定义:限定只能在表的一端进行插入,在表的另一端进行删除的线性表。

    修改原则:先进先出(FIFO)

图3-11 队列示意图


5267774

队列抽象数据类型:

ADT Queue iQ

{基本操作:

数据对象:D={ai|ai∈DataType,i=0,1,2,…,n-1,n≥0}

数据关系:R={<ai-1,ai>|ai-1,ai∈D,i=1,2,…,n-1}约定an-1为队尾,a0为队头。

(1)Queue InitQueue() 初始化并返回一个空队列;

(2)ClearQueue (Queue Q) 清空队列Q中的元素;

(3)int IsEmpty(Queue Q)

判断队列是否为空,返回队列空或非空标志;

(4)int IsFull(Queue Q) 判断队列是否满,返回队列满或不满标志;

(5)Queue InserQ(Queue Q, DataType x)

若队列未满,将数据元素x入队列;

(6)Queue DeleteQ(Queue Q) 若队列非空,删除队头元素;

(7)DataType GetHead(Queue Q) 若队列非空,返回队列Q中的队头元素;

(8)DataType GetRear(Queue Q) 若队列非空,返回队列Q中的队尾元素;

} ADT Queue


3 2 2

3.2.2 顺序队列与循环队列

  • 数组定义队列:queue[max]

  • 规定:front指向队头元素位置

    rear指向队尾元素的下一个空位

  • 初始:front=rear=0

  • 队空:front=rear

  • 队满:rear=max

max-1

1

0


3 2 2 2

3.2.2 顺序队列与循环队列2

(a)空队列 (b)队列中有3个元素 (c)队列3个元素出队 (d)队满

图3-12 顺序队列各种情况


3 2 2 3

3.2.2 顺序队列与循环队列3

  • 假溢出的问题:用循环队列来解决

  • 下一个rear计算公式:rear=(rear+1)mod max


3 2 2 4

3.2.2 顺序队列与循环队列4

  • 循环队列队空队满标志冲突的问题:

  • 以牺牲一个存储空间为代价,当判断到(下一个rear==front)时,即认为队满。

  • 实际上,max个空间只存放了(max-1)个元素


5267774

3.2.2 顺序队列与循环队列5

  • 循环队列队空、队满标志:

  • 队满:(rear+1)mod max==front

  • 队空:front=rear

    冲突解决!!


5267774

练习:

  • 设循环队列容量为70(序号0~69),现经过一系列的入队和出队后,问下列情况下循环队列中各有几个元素?

    (1)front=14,rear=21

    (2)front=23,rear=12


5267774

基于C语言的顺序队列的类型定义:

00 #define MAX 100

01 typedef struct

02 {

03 DataType elem[MAX];

/* 定义数组依次存放队列里的数据元素 */

04 int front; /* 指向队头元素的下标 */

05 int rear; /* 指向队尾元素的下一个空位 */

06 }Queue;


5267774

基本操作:初始化队列

00 Queue InitQueue()

01 {

02 Queue Q;

03 Q.front=Q.rear=0;

/* 队列初始化时front=rear=0 */

04 return(Q);

05 }


5267774

基本操作:进队列

00 Queue InserQ (Queue Q, DataType x)

01 {

02 if ((Q.rear+1) % MAX== Q.front)

03 printf(“队列已满,无法进队!”);

04 else

05 {

06 Q.elem[Q.rear]=x; /* x进队列 */

07 Q.rear = (Q.rear+1) % MAX;

/* rear指向队尾下一个空位 */

08 }

09 return(Q);

10 }


5267774

基本操作:出队列

00 Queue DeleteQ (Queue Q)

/* 若栈非空,删除栈顶元素 */

01 {

02 if (Q.rear == Q.front)

03 printf(“队列是空的,无法出队!”);

04 else

05 Q.front=(Q.front+1)% MAX;

06 return(Q);

07 }


5267774

基本操作:访问队头元素

00 DataType GetHead (Queue Q)

/* 若队列非空,取队头元素赋值给x */

01 {

02 if (Q.rear == Q.front)

03 printf(“队列是空的,无法取队头!”);

04 else

05 x=Q.elem[Q.front];

06 return(x);

07 }


3 2 3

3.2.3 队列链式存储结构

  • 1 struct node

  • 2 { DataType data; /* 链队列结点数据域类型及名称 */

  • 3 struct node *next; /* 指针域类型及名称,指向下一结点 */

  • 4 };

  • 5 typedef struct node QueueNode;

  • 6 struct node2

  • 7 { QueueNode *front;

  • 8 QueueNode *rear;

  • 9 };

  • 10 typedef struct node2 Queue;


5267774

front

rear

基本操作:链队列初始化

00 Queue InitQueue()

01 {

02 Queue Q;

03 Q.front=( QueueNode *) malloc (sizeof(QueueNode));

04 Q.front->next=NULL;

05 Q.rear=Q.front

06 return(Q);

07 }


5267774

基本操作:进队列

00 Queue InserQ(Queue Q, DataType x)

01 {

02 QueueNode *p;

03 p=(QueueNode *) malloc (sizeof(QueueNode));

04 p->data=x; /* x存储到新结点p中 */

05 p->next=NULL; /* p的指针域置空 */

06 Q.rear->next=p; /* 队尾指针域指向p,p成为新的队尾 */

07 Q.rear=p; /* 队尾指针rear指向p,保证rear指向新的队尾 */

08 return(Q);

09 }


5267774

基本操作:出队列

00 Queue DeleteQ(Queue Q)

01 {

02 QueueNode *p;

03 if (Q.front==Q.rear) /* 判断队列是否为空 */

04 { …… }

08 p= Q.front->next; /* p指向队头结点,待删除 */

09 Q.front->next=p->next; /* top结点指针域指向p的后继 */

10 if (p== Q.rear) /* 如被删结点也是队尾结点 */

11 Q.rear= Q.front; /* rear指向头结点 */

12 free(p); /* 释放p占用的空间 */

13 return(Q);

14 }


3 2 4

3.2.4 队列应用

1.共享打印机

2.CPU资源分配


3 2 4 2

3.2.4 队列的应用2

3.打印杨辉三角

过程分3步骤:

(1)第6行中的第一个元素1进队。

(2)输出第5行中的前5个元素,并生成第6行中的5个元素。循环5次,做:输出队头,队头出队列赋值给x,x与新队头相加进队列。

(3)输出第5行中的最后一个元素1并换行,出队列。

(4)第6行中的最后一个元素1进队。

程序见3.2.4节算法3-21。


5267774

基于循环队列的杨辉三角显示:


5267774

练习:

  • 设有一顺序栈S,元素s1,s2,s3,s4,s5,s6依次入栈,如果出栈顺序为s2,s3,s4,s6,s5,s1,栈的容量至少为( )。

    A. 2B. 3

    C. 5D. 6

B


5267774

  • 设有一栈已有a1,a2,a3三个元素,栈顶为a3,a4正等待入栈,则以下序列是不可能的出栈序列是( )。

    A. a3,a1,a4,a2B. a3,a2,a4,a1

    C. a3,a4,a2,a1D. a4,a3,a2,a1

A


5267774

  • ( )在链队中,即使不设置尾指针也能进行入队操作;

  • ( )在带头结点的链队中进行出队操作,不会改变头结点地址front的值;

  • ( )循环队列中元素个数为rear-front

T

T

F


5267774

  • 以S和X分别代表入栈和出栈,对输入序列a、b、c、d、e进行一系列栈操作SSXSXSSXXX后得到的序列为:

    b c e d a


5267774

  • 在一个链队中,如以f和r分别代表队头、队尾指针,则插入s结点的操作为:

    r->next=s; r=s;


5267774

本章小结

本章基本内容


  • Login