第 4 章 栈和队列

68
第4第 第第第第 第第第第 第第 第第第第第

Upload: kasi

Post on 19-Mar-2016

137 views

Category:

Documents


2 download

DESCRIPTION

第 4 章 栈和队列. 栈 栈的应用 队列 队列的应用. 栈的定义. 栈: 限定仅在表尾进行插入和删除的线性表 ,又称后进先出( Last In First 0ut 或简称 LIFO )的线性表。 栈顶: 允许进行插入和删除的一端。 栈底: 不允许插入和删除的另一端 。 实例:. 栈的运算. 初始化: 建立一个空栈。 入栈 : 在栈中加入一个新元素。 出栈: 删除栈中的栈顶元素。 取栈顶: 读栈中的栈顶元素。 判空: 测试栈是否为空。. 栈的表示方式. 静态的数组表示: 栈的顺序存储结构,常常以一个固定大小的数组来表示栈。 - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 第 4 章  栈和队列

第 4 章 栈和队列栈栈的应用队列队列的应用

Page 2: 第 4 章  栈和队列

栈的定义栈:限定仅在表尾进行插入和删除的线性表 ,又称后进先出( Last In First 0ut 或简称 LIFO )的线性表。栈顶:允许进行插入和删除的一端。 栈底:不允许插入和删除的另一端 。实例:

Page 3: 第 4 章  栈和队列

栈的运算 初始化:建立一个空栈。入栈 :在栈中加入一个新元素。出栈: 删除栈中的栈顶元素。取栈顶:读栈中的栈顶元素。判空:测试栈是否为空。

Page 4: 第 4 章  栈和队列

栈的表示方式 静态的数组表示:栈的顺序存储结构,常常以一个固定大小的数组来表示栈。动态的链表表示:用链表的结构来表示栈。

Page 5: 第 4 章  栈和队列

栈的顺序存储结构顺序栈:用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,设指针 top 指示栈顶元素在顺序栈中的位置。顺序栈数据结构可表示为:Typedef struct{int stacksize ;Selemtype *bottom ;SelemType *top ; } SqlStack; /* 顺序栈类型定义 */Sqlstack *S; /*S 是顺序栈类型指针 */

Page 6: 第 4 章  栈和队列

顺序栈中数据元素和栈顶指针之间的对应关系

Page 7: 第 4 章  栈和队列

静态数组实现栈结构 #define maxsize 64 /* 栈的最大容量 */typedef datatype int; /* 栈元素的数据类型 */typedef struct { datatype data[maxsize]; int top; int base;} seqstack; /* 顺序栈定义 */seqstack *s; /* 顺序栈的实现 */

Page 8: 第 4 章  栈和队列

顺序栈的模块说明 置空栈(栈的初始化)操作:initstack(s) seqstack *s; {

datatype data[maxsize];s->top=0;s->base=0;

}

Page 9: 第 4 章  栈和队列

判栈空操作:int empty(s)seqstack *s; { if(s->top==s->base) return true; else return false;}

Page 10: 第 4 章  栈和队列

进栈操作:seqstack *Push(s,x) /* 将元素 x 插入顺序栈 s 的顶部*/seqstack *s;datatype x;{ if (s->top==maxsize) { printf("overflow"); return NULL; } else { s->data[s->top]=x; s->top++; } return s; }

Page 11: 第 4 章  栈和队列

出栈操作:Datatype Pop(s , e) /* 若栈非空 , 删除栈顶元素,用 e 返回其值 */seqstack *s;datatype e;{ if (empty(s)) { printf("underflow");return NULL; /* 下溢 */ } else { s->top--; e= s->data[s->top] ; return(e); }}

Page 12: 第 4 章  栈和队列

取栈顶操作: Datatype GetTop(s) /* 取顺序栈 s 的栈顶 */seqstack *s;{ if (empty(s)) { printf("stack is empty");/* 空栈 */return null; } else return(s->data[--s->top]); }

Page 13: 第 4 章  栈和队列

多栈共享空间 实例:假定有两个栈共享一个数组 S[0 ,…, MAXSIZE-1]使第一个栈使用数组空间的前面部分,并使栈底在前;而使第二个栈使用数组空间的后面部分,并使栈底在后,这样实现了多栈共享空间;其空间分配示意图如图所示。

Page 14: 第 4 章  栈和队列

共享空间存储结构的定义:typedef datatype int; /* 栈元素的数据类型 */#define maxsize 64 /* 栈的最大容量 */typedef struct { datatype data[maxsize]; int top1,top2;}dstack;初始化操作:InitDstack ( dstack *s ){ s->top1=0; s->top2=maxsize-1 ;}

Page 15: 第 4 章  栈和队列

进栈操作:PushDstack ( dstack*s , char ch , datatype x ){/* 把数据元素 x 压入栈 s 的左栈或右栈 */ if ( s->top2 - s->top1==1 ) return 0 ; /* 栈已满 */ if ( ch==’s1’ ) { s->data[s->top1]=x;s->top1= s->top1+1; return 1; }/* 进栈 s1*/ if ( ch==’s2’ ) { s->data[s->top2]=x; s->top2= s->top2-1; return 1; }/* 进栈 s2*/}

Page 16: 第 4 章  栈和队列

出栈操作:popdstack(dstack *s,char ch){/* 从栈 S1 或 S2 取出栈顶元素并返回其值 */ if (char=’s1’) { if(s->top1==0) return null;/* 栈 s1 已空 */ else { s->top1= s->top1-1 ; return ( s->data[s->top1] ) ; }}/*s1 出栈 */

Page 17: 第 4 章  栈和队列

if(char=’s2’){ if(s->top2>maxsize-1) return null;/* 栈 s2 已空 */

else { s->top2= s->top2+1; return ( s->data[s->top2] ) ; } }/*s2 出栈 */}

Page 18: 第 4 章  栈和队列

栈的链式存储结构链栈 : 即栈的链式存储结构 ; 是运算受限制的单链表,其插入和删除操作仅限于表头位置上进行。 语法说明:Typedef datatype int;Typedef struct node{ datatype data; struct node *next;}linkstack;/* 链栈结点类型 */linkstack *top;

Page 19: 第 4 章  栈和队列

进栈操作:Linkstack pushlinkstack(top,w) /* 将元素 w 插入链栈 top 的栈顶 */Linkstack *top;Datatype X;{ linkstack *p; p=malloc(sizeof(linkstack));/* 生成新结点 *p */ p->data=w; p->next=top; return p;/* 返回新栈顶指针 */}/*pushlinkstack*/

Page 20: 第 4 章  栈和队列

出栈操作: linkstack poplinkstack(top,x) /* 删除链栈 top 的栈顶结点 */ linkstack top; datatype *x; /* 让 x 指向栈顶结点的值,返回新栈指针 */{ linkstack *p; if (top==null) { printf(“ 空栈,下溢” ) ; return null; } else { *x=top->data; /* 将栈顶数据存入 *x */ p=top; /* 保存栈顶结点地址 */ top=top->next; /* 删除原栈顶结点 */ free(p); /* 释放原栈顶结点 */ return top; /* 返回新栈顶指针 */ } }/*poplinkstack*/

Page 21: 第 4 章  栈和队列

置栈空: Void InitStack(LinkStack *S) { S->top=NULL; } 判栈空: int StackEmpty(LinkStack *S) { if ( S->top==NULL) return 0; else return 1; }

Page 22: 第 4 章  栈和队列

取栈顶元素: datatype StackTop(LinkStack *S) { if(StackEmpty(S)) Error("Stack is empty.") return S->top->data;

}

Page 23: 第 4 章  栈和队列

顺序栈和链式栈的比较顺序栈和链式栈的所有操作的时间相同 ,都是常数级的时间。初始化一个线性栈必须首先声明一个固定长度 , 在栈不够满时 ,就浪费了一部分存储空间;而链式栈无此问题。两个栈共享空间时,顺序栈发生上溢的概率比链式栈要小得多。

Page 24: 第 4 章  栈和队列

栈的应用迷宫问题算术表达式求值子程序的调用和返回数制转换行编辑

Page 25: 第 4 章  栈和队列

迷宫问题迷宫的表示方法:

用一个二维数组 maze[Max_Row][Max_Col] 来表示一个迷宫,其中数组元素 0代表可通行的路径, 1代表障碍;定义左上角为入口,右下角为出口。 例:

Page 26: 第 4 章  栈和队列

当位于 (i,j) 时,可能的移动方向:

移动方向的声明方式:Typedef struct{ int vert; int horiz;}offsets;offsets move[4];

Page 27: 第 4 章  栈和队列

移动方向的数组表示:

计算下一位置:假设现在的位置在 maze[row][col] ,下一步的位置在 maze[next_row][next_col] ,则可利用 direct变量知道 next_row 和 next_col 两个值:next_row=row+move[direct].vert;next_col=col+move[direct].horiz;

Page 28: 第 4 章  栈和队列

试错法:如果新的位置没有障碍,则将这个位置记录下来,反之,则尝试下一个方向,如果每一个方向都走不通的话,则表示这个结点是一个死点,将此结点删除。由此可见,由起点走到终点的是经过一连串的尝试错误,才找到通路的,这种查找方法称为试错法。回溯法:当从某一点开始,找不到一组解时,就回到前一个结点,再重新从这点出发去尝试下一个未尝试的可能路径。

迷宫问题中涉及的方法

Page 29: 第 4 章  栈和队列

算术表达式求值 表达式:由运算符、运算对象和界限符组成的有意义的式子 , 如: 5 十 4*3一 9 / 3。 算符优先法:根据运算优先关系的规定来实现对表达式的编译或解释执行。

Page 30: 第 4 章  栈和队列

设置工作栈: StackR:用于寄存运算符 StackD :用于寄存运算对象或运算结果

算法思想:首先置 StackD 为空栈,起始符“ $” 为运算符栈的栈底元素。依次读入表达式中每个字符,若是运算对象则进 StackD 栈 ; 若是运算符,则和 StackR栈的栈顶运算符比较优先权,若优先级高于栈顶元素则进栈,否则输出栈顶元素。从 StackD 中相应的输出两个运算对象作相应运算。然后再与 StackR中的栈顶元素进行优先级比较,以此类推,直至整个表达式求值完毕。

Page 31: 第 4 章  栈和队列

实例:利用上述算法对算术表达式 4*(8+3)求值 ,其操作如下:

Page 32: 第 4 章  栈和队列
Page 33: 第 4 章  栈和队列

子程序的调用和返回主调函数运行时调用另一个函数,在程序流转入被调函数之前,系统会作:

将调用函数时的所有实参、临时变量,返回地址等保存。为被调函数的变量分配存储空间。执行被调函数。

算法思想:系统将程序运行的数据存入栈中。当函数调用时,把该函数的实参、临时变量,返回地址等数据压入栈顶。当调函数执行完毕时:系统将运行时刻栈栈顶的活动结构退栈,并根据退栈的活动结构中所保存的返回地址将程序的控制权转移给调用者继续执行。

Page 34: 第 4 章  栈和队列

数制转换定义:将十进制数转换成其它进制的数的方法 ,利用公式 N=(N div d)*d +N mod d 。算法:void conversion() { {// 假设输入是非负的十进制整数,输出等值的 8进制数 Linkstack S; node e; int n; InitStack(&S); scanf("%d",&n); Push(S,0);

Page 35: 第 4 章  栈和队列

while(n){ // 从右向左产生 8进制的各位数字,并将其进栈 Push(S,n%8); n=n/8; } printf("the result is: ",n); while(!StackEmpty(*S)) { // 栈非空时退栈输出 Pop(S,&e); printf("%d",e); }}

Page 36: 第 4 章  栈和队列

行编辑定义:接受用户从终端输入的程序或数据,并存入用户的数据区,允许用户输入出错时可以及时更正;可以约定#为退格符,以表示前一个字符无效,@为退行符,表示当前行所有字符均无效。行编辑算法:void LineEdit() { SqlStack S,T; char str[1000]; int strlen=0; char e; char ch; InitStack(&S); InitStack(&T); ch=getchar();

Page 37: 第 4 章  栈和队列

while(ch!=EOF) { while(ch!=EOF&&ch!='\n') { switch(ch) { case '#': Pop(S,&ch); break; case '@': ClearStack(S); break; default: Push(S,ch); break; } ch=getchar(); } if(ch=='\n') Push(S,ch); while(!StackEmpty(*S)) { Pop(S,&e); Push(T,e); } while(!StackEmpty(*T)) { Pop(T,&e); str[strlen++]=e; } if(ch!=EOF) ch=getchar(); } //end while(ch!=EOF) str[strlen]='\0'; printf("\n%s",str); }

Page 38: 第 4 章  栈和队列

队列概念:队列是一种先进先出( FIRST IN FIRST OUT 简称 FIFO )的线性表;只允许在表的一端进行插入,在另一端删除元素;允许插入的一端称为队尾( Rear ),允许删除的一端称为队头( Front )。 示意图:

Page 39: 第 4 章  栈和队列

队列的基本操作:构造空队列 InitQueue(Q)队列销毁 DestroyQueue ( Q)队列清空 ClearQueue ( Q)取头元素 GetHead ( Q, e )队列插入 Enqueue(Q, e)队头删除 Dequeue(Q, e)

双端队列:限定插入和删除操作在表的两端进行的线性表。

Page 40: 第 4 章  栈和队列

队列的顺序存储顺序队列:即队列的顺序存储结构 ,用一维数组来表示 ;附设两个指针 front 指向队列头元素的位置, rear 指针指向队列尾元素的位置。

Page 41: 第 4 章  栈和队列

顺序队列类型说明:typedef datatype int; #define maxsize 66 /* 队列的最大长度 */typedef struct{ datatype data[maxsize]; int front,rear; /* 确定队头队尾位置的两个变量 */}sequeue;/* 顺序队列的类型 */sequeue *q;初始化操作:initseq(sequeue *q){ datatype data[maxsize]; q.front=-1; q.rear=-1;}

Page 42: 第 4 章  栈和队列

队头删除操作:Delseq(sequeue *q){ if (q->front==q->rear) printf(“sequeue empty!”); else { q->front++; return(q->data[q->front]); }}

Page 43: 第 4 章  栈和队列

队尾插入操作 :insertseq ( sequeue *q , int x ){ if(q->rear >= maxsize-1) return Null;/* 队列已满 */ else { (q->rear)++; q->data[q->rear]=x; return OK; }}

Page 44: 第 4 章  栈和队列

队列假溢:尾指针已经指向队尾,但队列的前面部分仍有可用空间。 实例:

Page 45: 第 4 章  栈和队列

队列假溢解决算法:void seq_full(sequeue *q,int x){ int i; if(q->rear-q->front=maxsize) printf(“sequeue overflow!!”);/* 溢出 */ else { /* 所有数据向前移 */ for(i=0;i<q->rear-q->front;i++) q->data[i]= q->data[q->front+i+1]; q->rear=q->rear-q->front; q->front=-1; }}

Page 46: 第 4 章  栈和队列

循环队列:顺序队列臆造为一个环状的空间。实例:

队尾添加操作,队尾指针加 1 :q->rear=(q->rear+1)%maxsize队头删除操作,队头指针 1 :q->front=(q->front+1)%maxsize

Page 47: 第 4 章  栈和队列

循环队列的头尾指针分布:

判断队满条件:q->front=(q->rear+1)% maxsize

判断队空条件 : q->front= q->rear

Page 48: 第 4 章  栈和队列

循环队列运算算法置空队:InitQueue(q) sequeue *q; { datatype data[maxsize]; q->front=-1; q->rear=-1; }判队空:int QueueEmpty(q)sequeue *q; { if(q->rear==q->front) return OK; else return Null; }

Page 49: 第 4 章  栈和队列

取队头元素: datatype GetHead(q) sequeue *q; { if (empty(q)) { print("sequeue is empty"); return Null ; } else return (q->front+1) % maxsize; }

Page 50: 第 4 章  栈和队列

入队操作:int InQueue(q,x) /* 将新元素 x 插入队列 *q 的队尾 */sequeue *q;datatype x;{ if(q->front==(q->rear+1) % maxsize) { print("queue is full"); return NUll; } else { q->rear=(q->rear+1) % maxsize; q->data[q->rear]=x; } }

Page 51: 第 4 章  栈和队列

出队操作:datatype DelQueue(q) sequeue *q; { if (empty(q)) return NULL; else { q->front=(q->front+1)%maxsize; return(q->data[q -> front]); } }

Page 52: 第 4 章  栈和队列

队列的链式存储链队列:即用链表表示的队列。 链队列的结构类型:typedef int dadatype /* 定义数据类型 */typedef struct node /* 链表结点类型定义 */{ datatype data; struct node *next;}linklist;typedef struct{ linklist *front,*rear;}linkqueue;linkqueue *q;

Page 53: 第 4 章  栈和队列

实例:

置空队列:InitQueue(q) /* 生成空链队列 */linkqueue *q;{ q->front=malloc(sizeof(linklist)); q->front->next=NULL; q->rear=q->front; }

Page 54: 第 4 章  栈和队列

判队空:int QueueEmpty(q) linkqueue *q; { if (q->front==q->rear) return OK ; else return Null; }

Page 55: 第 4 章  栈和队列

取队头元素:datatype *Gethead(q)linkqueue *q; { if (empty(q)) { printf("queue is empty"); return NULL; } else { return(q->front->next->data); } }

Page 56: 第 4 章  栈和队列

入队操作:InQueue(q,x) /* 将结点 x 插入队列 *q 的尾端 */linkqueue *q; datatype x;{ q->rear->next=malloc(sizeof(linklist)); q->rear->next->data=x; q->rear=q->rear->next; q->rear->next=NULL;}

Page 57: 第 4 章  栈和队列

出队操作:Datatype DeQueue(q) /* 删除队头元素,并返回该元素的值 */linkqueue *q; { linklist *s;datatype e; if (empty(q)) return NULL; s=q->front->next; e=s->data; /* 用 e 保存返回值 */ if(s==q->rear) q->front=q->rear ; /* 如果只有一个结点,出 队后队列为空 */

else q->front->next=s->next; free(s);return e;/* 返回值 */

} }

Page 58: 第 4 章  栈和队列

循环队列概念:队尾结点的指针指向队首结点,构成了循环链队列。

Page 59: 第 4 章  栈和队列

插入操作:

Page 60: 第 4 章  栈和队列

插入算法:Insert_ cque(linkqueue *r, int x ){ /* 在结点 r 后面插入一个结点其数据域为 x*/ linkqueue *t; t=( sequeue *)malloc(size of(sequeue)); t->data=x; /* 生成一个结点 t*/ if(*r=null) { r=t; r->next=r; } else { t->next=r->next; r->next=t; }}

Page 61: 第 4 章  栈和队列

删除操作:

Page 62: 第 4 章  栈和队列

删除算法 :Del_cque(linkqueue r){ /* 删除队列中的头结点 , 即结点 r 的 next 结点 */ linkqueue *front; int e; if(*r=null) printf(“sequeue empty!!”);return Null ; else { front=r->next; /* 将指向头结点的指针赋给 front*/ if(front=*r) *r=null; else r->next=front->next; e=front->data; Free(front); Return (e); }}

Page 63: 第 4 章  栈和队列

优先队列优先队列的思想是将最重要的元素赋予最小的优先级(从代价角度)或最大的优先级(从利润角度)。优先队列最重要的应用是在堆排序中 。

Page 64: 第 4 章  栈和队列

队列的应用解决设备速度不匹配问题:为解决设备速度不匹配的问题,在内存中设置一缓冲区,并设计成循环队列结构,设置一队首指针和一队尾指针,初始时循环队列为空 ;计算机每处理完一批数据就将其加入到循环队列的队尾;打印机每处理完一个数据,就从循环队列的队首取出下一个要打印的数据。打印机来不及打印的数据就在缓冲区中排队等待。利用缓冲区,解决了计算机处理数据与打印机输出速度不匹配的矛盾。舞伴问题:

Page 65: 第 4 章  栈和队列

舞伴问题的问题描述:舞会上,男士们和女士们进入舞厅时,各自排成一队。跳舞开始时,依次从男队和女队的队头上各出一人配成舞伴。若两队初始人数不相同,则较长的那一队中未配对者等待下一轮舞曲。 舞伴问题的类型描述:

typedef struct{ char name[20]; char sex; // 性别, 'F'表示女性, 'M'表示男性 }Person;typedef Person datatype; // 队列中元素的数据类型为 Person

Page 66: 第 4 章  栈和队列

舞伴问题的算法:void DancePartner(Person dancer[],int num) {// 结构数组 dancer 中存放跳舞的男女, num 是跳舞的人数。 int i; Person p; Sequence *Mdancers,*Fdancers; InitQueue(&Mdancers);//男士队列初始化 InitQueue(&Fdancers);//女士队列初始化 for(i=0;i<num;i++) {// 依次将跳舞者依其性别入队 p=dancer[i]; if(p.sex=='F') EnQueue(&Fdancers.p); //排入女队 else EnQueue(&Mdancers.p); //排入男队 }

Page 67: 第 4 章  栈和队列

printf("The dancing partners are: \n \n"); while(!QueueEmpty(&Fdancers)&&!QueueEmpty(&Mdancers)) { // 依次输入男女舞伴名 p=DeQueue(&Fdancers); //女士出队 printf("%s ",p.name);//打印出队女士名 p=DeQueue(&Mdancers); //男士出队 printf("%s\n",p.name); //打印出队男士名 } if(!QueueEmpty(&Fdancers)) { //输出女士剩余人数及队头女士的名字 printf("\n %d waiting in next round.\n",Fdancers.count); p=QueueFront(&Fdancers); // 取队头 printf("%s will be the first to get a partner. \n",p.name); } else

Page 68: 第 4 章  栈和队列

if(!QueueEmpty(&Mdancers)) {//输出男队剩余人数及队头者名字 printf("\n There are%d men waiting for the next round.\n",Mdacers.count); p=GetHead(&Mdancers); printf("%s will be the first to get a partner.\n",p.name); } }