1 / 62

第三章 栈与队列

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

alvis
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. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第三章 栈与队列 两种重要的,限定操作的线性结构。

  2. 学习要点: • 栈的基本概念、结构特征和操作要点。 • 栈在顺序存储和链式存储结构下各种操作的实现,栈满与栈空判定条件。 • 队列基本概念、结构特征和操作要点。 • 队列在两种存储结构下各种操作如何实现,队满与队空判定条件。 • 循环队列基本概念,循环队列的队空与队满判定条件。 • 栈和队列的基本应用和应用要点。

  3. §3.1 栈 3.1.1 栈的基本概念 栈的例子:操作系统中中断现场的保留等。 定义:限定只能在表的一端进行插入或删除操作的线性表。 修改原则:后进先出(LIFO)

  4. 练习: • 设进栈顺序为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 × ×

  5. 栈的抽象数据类型: • 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

  6. 3.1.2 栈的顺序存储结构 • 数组定义栈:stack[max] • 规定:top指向栈顶元素所在位置 • 初始:top=-1(空栈) 空栈:top=-1,出栈下溢 栈满:top=max-1,进栈上溢 max-1 … 1 0

  7. 3.1.2 栈的顺序存储结构2 (a)空栈 (b)栈中有两个元素 (c)栈满 图3-2 顺序栈各种情况

  8. 3.1.2 栈的顺序存储结构3 用C语言定义栈的顺序存储结构如下: #define MAXSIZE 100 /* MAXSIZE指的是栈的最大长度 */ typedef struct {DataType elem[MAXSIZE]; /* 定义数组依次存放栈里的数据元素*/ int top; /* 指向栈顶元素的下标 */ }Stack;

  9. 基本操作:顺序栈初始化 算法要求返回一个空的栈,空栈中数据元素个数为0,top值为-1。 00 Stack InitStack() 01 { 02 Stack S; 03 S.top=-1; /* 空栈的top值为-1 */ 04 return(S); 05 }

  10. 已知栈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 }

  11. 基本操作:出栈 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 }

  12. 已知栈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 }

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

  14. 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; 设指针域指向次顶结点

  15. 3.1.4 栈的链式存储结构 图3-5 链栈

  16. 基本操作:链栈初始化 初始化链栈即建立一个空的链栈,只有top结点,其指针域为空。 00 StackNode *InitStack() 01 { 02 StackNode *top; 03 top=( StackNode *) malloc (sizeof(StackNode)); 04 top->next=NULL; 05 return(top); 06 }

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

  18. 基本操作:进栈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 }

  19. 基本操作:出栈

  20. 基本操作:出栈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 }

  21. 3.1.4 栈的应用 1.栈在递归程序中的应用——阶乘运算 递归问题的解决可以大致做这样的算法描述: if (递归结束条件) return (递归结束条件下的返回值); else return (递归计算公式);

  22. 3.1.4 栈的应用 1.栈在递归程序中的应用——阶乘运算 解释: ●递归计算公式:是大问题在变成次大问题中的表现出来的规律,要表达清楚这个大问题与下一级的次大问题有什么联系。 ●递归结束条件:解决递归问题中的分解不能无终止地分解下去,需有一个结束的条件。这样才可以由结束递归再返回层层解套,最终解决整个问题。递归的结束条件也称为递归出口。

  23. 1 (n=0,1) n!= n*(n-1)! (n>1) 3.1.4 栈的应用 例子:n的阶乘的计算

  24. 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 }

  25. 3.1.4 栈的应用2 2.栈在递归程序中的应用——Hanoi塔问题 解释Hanoi游戏: (1)设有三根杆子A,B,C。A杆上有n个盘子,从小到大相叠着,编号1—n; (2)每次移动一块盘子到另外一个杆上,但要求每个杆上的盘子只能是小的叠在大的上面; (3)把所有盘子从A杆全部移到C杆上,可利用B杆作为过渡。

  26. 3阶Hanoi问题求解过程:

  27. 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 }

  28. 3.1.4 栈的应用3 n阶Hanoi非递归算法见3.1.4节算法3-11。

  29. 3.1.4 栈的应用3 3.栈在括号匹配中的应用 问题理解: 假设一个算术表达式中包含圆括号、方括号和花括号三种类型的括号,编写一个算法,判别表达式中括号是否正确配对。 如“fha{bh(gf[hg]gt)q}gfr”是正确匹配的,“fds[fsd(gfd]g”则缺少“)”。

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

  31. θ1θ2 + - * ) + > < > - > > < > \ > > > > ( < < < = 3.1.4 栈的应用4 4.栈在表达式求值中的应用 (1)由计算规则得算符优先关系表 3+5*3 3+5+5 θ1:先来的算符 θ2:后来的算符 >

  32. 4.栈在表达式求值中的应用2 (2)原理 两工作栈:一个算符栈,一个操作数栈 a.读取到操作数,直接入操作数栈 b.读取到运算符,取运算符栈栈顶元素与该算符比较优先级: “<”:读取符号入运算符栈; “>”:运算符栈出一元素,操作数栈出两元素, 运算后,结果放回操作数栈; “=”:运算符栈出一元素,与之相抵消。

  33. 4.栈在表达式求值中的应用3 计算#3*(15-5)/2#栈的变化过程

  34. 4.栈在表达式求值中的应用2 (2)原理 算法结束标志:运算符栈为空,操作数栈有一元素,即为结果。 表达式求值算法见3.1.4节算法3-13。

  35. 上机(选做): • 输入十进制数,转换成R进制数。 • 输入一包含“(”和“)”的字符串,检测括号是否匹配(其中括号中能嵌套括号),并输出括号是否匹配的信息(匹配、缺少左括号、缺少右括号);

  36. 分析: • gets(fun) • while (fun[i]!=’\0’) • {if (fun[i]==’(‘):入栈; • if (fun[i]==‘)’): • if 栈空:错,缺左边,return; • else 出栈; • i=i+1; • } • 如栈空 对!!! • 否则 错,缺少右边

  37. §3.2 队列 3.2.1 队列基本概念 • 队列的例子:排队等。 定义:限定只能在表的一端进行插入,在表的另一端进行删除的线性表。 修改原则:先进先出(FIFO) 图3-11 队列示意图

  38. 队列抽象数据类型: 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

  39. 3.2.2 顺序队列与循环队列 • 数组定义队列:queue[max] • 规定:front指向队头元素位置 rear指向队尾元素的下一个空位 • 初始:front=rear=0 • 队空:front=rear • 队满:rear=max max-1 … 1 0

  40. 3.2.2 顺序队列与循环队列2 (a)空队列 (b)队列中有3个元素 (c)队列3个元素出队 (d)队满 图3-12 顺序队列各种情况

  41. 3.2.2 顺序队列与循环队列3 • 假溢出的问题:用循环队列来解决 • 下一个rear计算公式:rear=(rear+1)mod max

  42. 3.2.2 顺序队列与循环队列4 • 循环队列队空队满标志冲突的问题: • 以牺牲一个存储空间为代价,当判断到(下一个rear==front)时,即认为队满。 • 实际上,max个空间只存放了(max-1)个元素

  43. 3.2.2 顺序队列与循环队列5 • 循环队列队空、队满标志: • 队满:(rear+1)mod max==front • 队空:front=rear 冲突解决!!

  44. 练习: • 设循环队列容量为70(序号0~69),现经过一系列的入队和出队后,问下列情况下循环队列中各有几个元素? (1)front=14,rear=21 (2)front=23,rear=12

  45. 基于C语言的顺序队列的类型定义: 00 #define MAX 100 01 typedef struct 02 { 03 DataType elem[MAX]; /* 定义数组依次存放队列里的数据元素 */ 04 int front; /* 指向队头元素的下标 */ 05 int rear; /* 指向队尾元素的下一个空位 */ 06 }Queue;

  46. 基本操作:初始化队列 00 Queue InitQueue() 01 { 02 Queue Q; 03 Q.front=Q.rear=0; /* 队列初始化时front=rear=0 */ 04 return(Q); 05 }

  47. 基本操作:进队列 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 }

  48. 基本操作:出队列 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 }

  49. 基本操作:访问队头元素 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 }

  50. 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;

More Related