1 / 39

第三章 栈和队列

第三章 栈和队列. 学习要点 1. 掌握栈和队列这两种抽象数据类型的特点,并能在相应的应用问题中正确选用它们。 2. 熟练掌握栈类型的两种实现方法,即两种存储结构表示时的基本操作实现算法,特别应注意栈满和栈空的条件以及它们的描述方法。 3. 熟练掌握循环队列和链队列的基本操作实现算法,特别注意队满和队空的描述方法。 4. 理解递归算法执行过程中栈的状态变化过程。.  第三章 栈和队列 3.1 栈 3.2 队列. 返回主菜单. 进栈. 出栈. 栈顶. a n. ……. 栈 s=(a1,a2,……,an).

andren
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. 学习要点 1. 掌握栈和队列这两种抽象数据类型的特点,并能在相应的应用问题中正确选用它们。 2. 熟练掌握栈类型的两种实现方法,即两种存储结构表示时的基本操作实现算法,特别应注意栈满和栈空的条件以及它们的描述方法。 3. 熟练掌握循环队列和链队列的基本操作实现算法,特别注意队满和队空的描述方法。 4. 理解递归算法执行过程中栈的状态变化过程。

  3.  第三章 栈和队列 3.1 栈 3.2 队列 返回主菜单

  4. 进栈 出栈 ... 栈顶 an ……... 栈s=(a1,a2,……,an) a2 栈底 a1 栈和队列是两种特殊的线性表,是操作受限的线性表,称限定性DS • 3.1 栈(stack) • 栈的定义和特点 • 定义:限定仅在表尾进行插入或删除操作的线性表,表尾—栈顶,表头—栈底,不含元素的空表称空栈 • 特点:先进后出(FILO)或后进先出(LIFO)

  5. F E 5 5 5 top top top top top top top D 4 4 4 C 3 3 3 B top top top top top top 2 2 2 A 1 1 1 top=0 0 0 0 栈空 栈空 栈满 • 栈的表示和实现 • 顺序栈 • 实现:一维数组s[M] F E D C B A 出栈 进栈 设数组维数为M top=0,栈空,此时出栈,则下溢(underflow) top=M,栈满,此时入栈,则上溢(overflow) 栈顶指针top,指向实际栈顶 后的空位置,初值为0

  6. #define INITSIZE 100; #define INCREMENT 10; Typedef struct { SElemType *base; SElemType *top; int stacksize; }SqStack; • 实现:存储表示 • 初始化算法 Status InitStack(SqStack &S) { // 构造一个空栈S S.base=(SElemType *) malloc (INITSIZE * sizeof(SElemType)); if (!S.base) exit (OVERFLOW); //存储分配失败 S.top=S.base; S.stacksize=INITSIZE; return OK; }//InitStack

  7. 取栈项元素算法 Stack GetTop (SqStack S,SElemType &e) { //若栈不空,则用e返回S的栈顶元素,并返回OK, //否则返回ERROR; if (S.top==S.base) return ERROR; e=*(S.top-1); return OK; }//GetTop

  8. 0 M-1 栈1底 栈1顶 栈2顶 栈2底 int push(int s[],int x,int top) { if(top==M) { printf("overflow"); return(-M); } s[top]=x; return(++top); } • 入栈算法 • 出栈算法 int pop(int s[],int top,int *q) { if(top==0) { printf("stack empty"); return(0); } *q=s[--top]; return(top); } • 在一个程序中同时使用两个栈

  9. 栈顶 栈底 …... top ^ data link • 链栈 • 结点定义 typedef struct node { int data; struct node *link; }JD;

  10. top top 栈底 p x …... ^ • 入栈算法 JD *lzjz(JD *top,int x) { JD *p; p=(JD *)malloc(sizeof(JD)); p->data=x; p->link=top; top=p; return(p); }

  11. q top 栈底 top …... ^ • 出栈算法 JD *lztz(JD *top,int *p) { JD *q; if(top!=NULL) { q=top; *p=top->data; top=top->link; free(q); } return(top); }

  12. 8 159 8 8 19 2 2 3 7 例 把十进制数159转换成八进制数 余 7 3 top top top top 余 3 7 7 余 2 0 2 (159)10=(237)8 3 7 • 多进制输出: void conversion() { InitStack(S); scanf('%d",N); while (N) { Push(S,N%8); N=N/8; } while (!StackEmpty(S)) { Pop(S,e); printf("%d",e); } }//conversion

  13. 括号匹配的检验 假设表达式中充许括号嵌套,则检验括号是否匹配的方法可用“期待的急迫程度”这个概念来描述。例: [([ ] [ ])] • 行编辑程序 在编辑程序中,设立一个输入缓冲区,用于接受用户输入的一行字符,然后逐行存入用户数据区。允许用户输入错误,并在发现有误时可以及时更正。 top d a d • 回文游戏:顺读与逆读字符串一样(不含空格) 1.读入字符串 2.去掉空格(原串) 3.压入栈 4.原串字符与出栈字符依次比较 若不等,非回文 若直到栈空都相等,回文 字符串:“madam im adam”

  14. 行编辑程序算法如下:void lineedit( ){ initstack(S); ch=getchar( ); while(ch!=eof){ while(ch!=eof && ch!=‘\n’){ switch(ch){ case ‘#’ : pop(S,c); case ‘@’ : clearstack(S); default : push(S,ch); } ch=getchar( ); } clearstack(S); if(ch!=eof) ch=gethar( ); } destroystack(S); }//LineEdit

  15. 6 18 3 * 4 + - - - 12 6 6 6 2 操作数 操作数 操作数 操作数 操作数 运算符 运算符 运算符 运算符 运算符 • 表达式求值 中缀表达式 后缀表达式(RPN) a*b+c ab*c+ a+b*c abc*+ a+(b*c+d)/e abc*d+e/+ 中缀表达式:操作数栈和运算符栈 例 计算 2+4-3*6

  16. 15 3 top top top top top top 4 4 5 19 4 3 7 后缀表达式求值步骤: 1、读入表达式一个字符 2、若是操作数,压入栈,转4 3、若是运算符,从栈中弹出2个数,将运算结果再压入栈 4、若表达式输入完毕,栈顶即表达式值; 若表达式未输入完,转1 后缀表达式:435*+ 例 计算 4+3*5

  17. Y=2; Z=3; X=y+z*2; 如何对表达式求值呢? ? 栈的应用举例 • 表达式求值 • 问题的提出: 设计一个小计算器: 对键入的表达式进行求值。 高级语言中的赋值语句:变量=表达式;

  18. 如何确定运算符的运算顺序? ? • 表达式的构成 操作数+运算符+界符(如括号) • 表达式的求值: • 例 5+6 (1+2)- 4 • 按照四则运算法则,上述表达式的计算过程为: • 5+6 (1+2) - 4=5+6 3- 4 = 5+18-4= 23-4=19 3 2 1 4

  19. c2 + - * / ( ) # c1 + > > < < < > > - > > < < < > > * > > > > < > > / > > > > < > > ( < < < < < = ) > > > > > > < < < < < = # • 算符优先关系表 • 表达式中任何相邻运算符c1、c2的优先关系有:c1<c2:c1的优先级低于c2 c1=c2:c1的优先级等于c2 c1>c2:c1的优先级高于c2 算符间的优先关系表 注: c1c2是相邻算符, c1在左, c2在右

  20. 算符优先算法 用两个栈分别保存扫描过程中遇到的操作数和运算符 5 + 4  (1 + 2) - 6 从左向右扫描表达式: 遇操作数——保存; 遇运算符号cj——与前面的刚扫描过的运算符ci比较 若ci<cj 则保存cj,( 因此已保存的运算符的优先关系为 c1<c2<c3<c4。。)若ci>cj 则说明ci是已扫描的运算符中优先级最高者,可进行运算;  若ci = cj 则ci为(,cj 为 ),说明括号内的式子已计算完,需要消去括号 后面也许有优先级更高的算符 +  ( + 后保存的算符有优先级高

  21. 在算符优先算法中,建立了两个工作栈。一个是OPTR栈,用以保存运算符.一个是OPND栈,用以保存操作数或运算结果。int express ( ) {//运算数栈,OP为运算符集合。 InitStack(OPTR); Push (OPTR, ‘# ‘); InitStack(OPND); w=getchar( ); While(w!=‘ #’ || GetTop(OPTR)!=‘#’) {if(! In(w,OP)){Push(OPND,w);w=getchar();} //不是运算符则进栈 else // In(w, OP)判断c是否 是运算符的函数

  22. 续switch (Precede(GetTop(OPTR), w) { case ‘<‘:// 新输入的算符 w 优先级高,w 进栈 Push(OPTR, w ); w =getchar( ); break; case ‘=‘: // 去括号并接收下一字符 x=Pop(OPTR); w=getchar( ); break; case ‘>’://新输入的算符c优先级低,即栈顶算符优先权高 //出栈并将运算结果入栈OPND op=Pop(OPTR); b=Pop(OPND); a= Pop(OPND); Push(OPND, Operate(a, op, b)); break; } } return GetTop(OPND); }

  23. 子过程2 子过程1 子过程3 t s s 主程序 r r r s t r r s r • 栈的应用 • 过程的嵌套调用

  24. 递归过程及其实现 • 递归:函数直接或间接的调用自身叫~ • 实现:建立递归工作栈 例 递归的执行情况分析 void print(int w) { int i; if ( w!=0) { print(w-1); for(i=1;i<=w;++i) printf(“%3d,”,w); printf(“/n”); } } 运行结果: 1, 2,2, 3,3,3,

  25. w w 1 0 w print(0); 2 返回 w (4)输出:1 (4)输出:1 主程序 print(1); 3 (3) 输出:2, 2 (3) 输出:2, 2 print(2); w=3; (2) 输出:3, 3, 3 (2) 输出:3, 3, 3 print(w) (1) (1) top (3)w=1 (1 ) 3 (4)w=0 (2)w=2 (3)w=1 (2) 2 (1)w=3 (2)w=2 (1) 3 top (1)w=3 top (4) 0 top (3) 1 (3) 1 top (2)w=2 (2) 2 (2) 2 (1)w=3 (1) 3 (1) 3 (1)w=3 top top top 结束 递归调用执行情况如下:

  26. n x y z 返回地址 • Tower of Hanoi问题 • 问题描述:有A,B,C三个塔座,A上套有n个直径不同的圆盘,按直径从小到大叠放,形如宝塔,编号1,2,3……n。要求将n个圆盘从A移到C,叠放顺序不变,移动过程中遵循下列原则: • 每次只能移一个圆盘 • 圆盘可在三个塔座上任意移动 • 任何时刻,每个塔座上不能将大盘压到小盘上 • 解决方法: • n=1时,直接把圆盘从A移到C • n>1时,先把上面n-1个圆盘从A移到B,然后将n号盘从A移到C,再将n-1个盘从B移到C。即把求解n个圆盘的Hanoi问题转化为求解n-1个圆盘的Hanoi问题,依次类推,直至转化成只有一个圆盘的Hanoi问题 • 算法执行情况: • 递归工作栈保存内容:形参n,x,y,z和返回地址 • 返回地址用行编号表示

  27. 1 2 3 A B C A B C 3 A B C 0 1 A B C 6 2 A C B 6 3 A B C 0 2 A C B 6 3 A B C 0 3 A B C 0 2 A C B 6 main() { int m; printf("Input number of disks”); scanf("%d",&m); printf(”Steps : %3d disks”,m); hanoi(m,'A','B','C'); (0) } void hanoi(int n,char x,char y,char z) (1) { (2) if(n==1) (3) move(1,x,z); (4) else{ (5) hanoi(n-1,x,z,y); (6) move(n,x,z); (7) hanoi(n-1,y,x,z); (8) } (9) }

  28. A B C A B C 3 A B C 0 2 A C B 6 1 C A B 8 3 A B C 0 2 A C B 6 3 A B C 0 3 A B C 0 2 A C B 6 main() { int m; printf("Input the number of disks scanf("%d",&m); printf("The steps to moving %3d hanoi(m,'A','B','C'); (0) } void hanoi(int n,char x,char y,char z) (1) { (2) if(n==1) (3) move(1,x,z); (4) else{ (5) hanoi(n-1,x,z,y); (6) move(n,x,z); (7) hanoi(n-1,y,x,z); (8) } (9) }

  29. A B C A B C 3 A B C 0 2 B A C 8 3 A B C 0 2 B A C 8 1 B C A 6 3 A B C 0 2 B A C 8 3 A B C 0 main() { int m; printf("Input the number of disks scanf("%d",&m); printf("The steps to moving %3d hanoi(m,'A','B','C'); (0) } void hanoi(int n,char x,char y,char z) (1) { (2) if(n==1) (3) move(1,x,z); (4) else{ (5) hanoi(n-1,x,z,y); (6) move(n,x,z); (7) hanoi(n-1,y,x,z); (8) } (9) }

  30. A B C A B C 3 A B C 0 2 B A C 8 3 A B C 0 2 B A C 8 1 A B C 8 2 B A C 8 3 A B C 0 3 A B C 0 Hanoi.c D:\fengyi\bkc\power\power.c 栈空 main() { int m; printf("Input the number of disks scanf("%d",&m); printf("The steps to moving %3d hanoi(m,'A','B','C'); (0) } void hanoi(int n,char x,char y,char z) (1) { (2) if(n==1) (3) move(1,x,z); (4) else{ (5) hanoi(n-1,x,z,y); (6) move(n,x,z); (7) hanoi(n-1,y,x,z); (8) } (9) }

  31. 出队 a1 a2 a3…………………….an 入队 队列Q=(a1,a2,……,an) front rear 出队 出队 a1 a2 a3…………………….an 入队 入队 端1 端2 • 3.2 队列 • 队列的定义及特点 • 定义:队列是限定只能在表的一端进行插入,在表的另一端进行删除的线性表 • 队尾(rear)——允许插入的一端 • 队头(front)——允许删除的一端 • 队列特点:先进先出(FIFO) • 双端队列

  32. 头结点 队头 队尾 …... front ^ rear typedef struct Qnode{ QElemType data; struct Qnode *next; }Qnode,*QueuePtr; Typedef struct{ QueuePtr front; QueuePtr rear; }//LinkQueue; • 链队列 • 结点定义 设队首、队尾指针front和rear, front指向头结点,rear指向队尾

  33. ^ ^ x出队 x y ^ rear front y出队 空队 x入队 x ^ rear rear front front front rear y入队 x y ^ rear front

  34. 入队算法 Status EnQueue(LinkQueue &Q,QElemType e){ p=(QueuePtr) malloc (sizeof(QNode)); if (!p) exit (OVERFLOW); p->data=e; p->next=NULL; Q.rear->next=p; Q.rear=p; return OK; } • 出队算法 Status DeQueue(LinkQueue &Q,QElemType &e){ if (Q.front==Q.rear) return ERROR; p=Q.front->next; e=p->data; Q.front->next=p->next; if (Q.rear==p) Q.rear=Q.front; free(p); return OK; }

  35. rear J6 J5 5 5 5 5 rear J4 4 4 4 4 rear rear rear J3 front 3 3 3 3 front front front J2 2 2 2 2 J1 1 1 1 1 front=0 rear=0 front front J4,J5,J6入队 J1,J2,J3出队 J1,J1,J3入队 0 0 0 0 队空 • 队列的顺序表示和实现 • 实现:用一维数组实现sq[M] J3 J2 J1 设两个指针front,rear,约定: rear指示队尾元素; front指示队头元素位置 初值front=rear=0 空队列条件:front==rear 入队列:sq[++rear]=x; 出队列:x=sq[++front];

  36. M-1 rear …... 0 1 …... front • 存在问题 设数组维数为M,则: • 当front=0,rear=M-1时,再有元素入队发生溢出——真溢出 • 当front0,rear=M-1时,再有元素入队发生溢出——假溢出 • 解决方案 • 队首固定,每次出队剩余元素向下移动——浪费时间 • 循环队列 • 基本思想:把队列设想成环形,让sq[0]接在sq[M-1]之后,若rear+1==M,则令rear=0; • 实现:利用“模”运算 • 入队: rear=(rear+1)%M; sq[rear]=x; • 出队: front=(front+1)%M; x=sq[front]; • 队满、队空判定条件

  37. front rear 5 4 0 3 1 2 rear J5 J6 J4 5 4 0 J4,J5,J6出队 3 1 2 front J7,J8,J9入队 J5 初始状态 J6 J4 5 4 0 3 1 J7 J9 2 J8 front rear 队空:front==rear 队满:front==rear 解决方案: 1.另外设一个标志以区别队空、队满 2.少用一个元素空间: 队空:front==rear 队满:(rear+1)%M==front

  38. 入队算法: Status EnQueue(SqQueue &Q,QElemType e) { if ((Q.rear+1) % MAXSIZE ==Q.front) return ERROR; Q.base[Q.rear]=e; Q.rear=(Q.rear+1) % MAXSIZE; return OK; } • 出队算法: Status DeQueue (SqQueue &Q,QElemType &e) { if (Q.front==Q.rear) return ERROR; e=Q.base[Q.front]; Q.front= (Q.front+1) % MAXSIZE; return OK; }

  39. 第三章 结 束 返回主菜单

More Related