50
第第第 第第第第 第第第 第第第第 3.1 栈 3.1.1 栈栈栈栈栈栈栈栈栈栈 3.1.2 栈栈栈栈栈栈栈 3.2 栈栈栈栈栈栈 3.3 栈栈 3.3.1 栈栈栈栈栈栈栈栈栈栈栈 3.3.2 栈栈栈—栈栈栈栈栈栈栈栈栈栈 3.3.3 栈栈栈栈— 栈栈栈栈栈栈栈栈 栈栈

第三章 栈和队列

  • Upload
    serge

  • View
    136

  • Download
    0

Embed Size (px)

DESCRIPTION

第三章 栈和队列. 3.1 栈 3.1.1 抽象数据类型栈的定义 3.1.2 栈的表示和实现 3.2 栈的应用举例 3.3 队列 3.3.1 抽象数据类型队列的定义 3.3.2 链队列 — 队列的链式表示与实现 3.3.3 循环队列 — 队列的顺序表示与 实现. 栈和队列也是线性表,只不过是两种 操作受限 的线性表。 如果从数据类型角度看,他们是和线性表大不相同的两类重要的抽象数据类型。 栈和队列广泛应用于各种软件系统中。. 出栈. 进栈. a n. 栈顶. a 2. 栈底. a 1. 3.1 栈. - PowerPoint PPT Presentation

Citation preview

Page 1: 第三章    栈和队列

第三章 栈和队列第三章 栈和队列3.1 栈 3.1.1 抽象数据类型栈的定义 3.1.2 栈的表示和实现3.2 栈的应用举例3.3 队列 3.3.1 抽象数据类型队列的定义 3.3.2 链队列—队列的链式表示与实现 3.3.3 循环队列— 队列的顺序表示与 实现

Page 2: 第三章    栈和队列

栈和队列也是线性表,只不过是两种操作受限操作受限的线性表。

如果从数据类型角度看,他们是和线性表大不相同的两类重要的抽象数据类型。

栈和队列广泛应用于各种软件系统中。

Page 3: 第三章    栈和队列

3.1 3.1 栈栈栈 -- 是限制仅在线性表的一端进行插入和

删除运算的线性表。– 栈顶( top ) -- 允许插入和删除的一端。– 栈底( bottom)-- 不允许插入和删除的一端。– 空栈 -- 表中没有元素。

an

...

a2

a1栈底

栈顶

出栈 进栈

• 栈的特点:栈的特点: 后进先出 后进先出 ((LIFOLIFO))

Page 4: 第三章    栈和队列

栈的基本运算栈的基本运算1. 初始化 — 创建一个空栈;

2. 判栈空 — 判断栈是否为空栈;

3. 进栈 — 往栈中插入(或称推入)一个元素;

4. 退栈 — 从栈中删除(或称弹出)一个元素;

5. 取栈顶元素

栈的抽象数据类型的定义见教材 p45 。

Page 5: 第三章    栈和队列

3.1.2 3.1.2 栈的表示与实现栈的表示与实现顺序栈顺序栈

– 栈的顺序存储结构简称为顺序栈。可用数组来实现顺序栈。

– 因为栈底位置是固定不变的,所以可以将栈底位置设置在数组的两端的任何一个端点;

– 栈顶位置是随着进栈和退栈操作而变化的, 故需用一个整型变量 top 表示栈顶位置。

Page 6: 第三章    栈和队列

顺序栈的类型定义如下:顺序栈的类型定义如下:

静态分配方式: # define STACKSIZE 100

typedef char SElemType;

typedef struct {

SElemType elem[STACKSIZE ];

int top;

}SqStack;

Page 7: 第三章    栈和队列

动态分配方式: #define INIT_SIZE 100;

#define INCREMENT 10;

typedef char SElemType;

typedef struct {

SElemType *base ;

SElemType *top ;

int stacksize ; // 栈的可用最大容量 }SqStack;

Page 8: 第三章    栈和队列

顺序栈的基本操作的实现顺序栈的基本操作的实现

1 、置空栈置空栈Status InitStack(SqStack &S){

if(!(S.base=(SElemType *)malloc(INIT_SIZE *sizeof(SElemType))));

exit(OVERFLOW); S.top = S.base; S.stacksize = INIT_SIZE; return OK; }

Page 9: 第三章    栈和队列

22 、、判 栈 空判 栈 空

Status StackEmpty(SqStack S) { if( S.top == S.base ) return TRUE; else return FALSE; }

Page 10: 第三章    栈和队列

33 、、取栈顶元素取栈顶元素

Status GetTop(SqStack S, SElemType &e) if(S.top>S.base)

{ e =*(S.top-1); return OK; } else return ERROR;}

Page 11: 第三章    栈和队列

44 、、进 栈进 栈 Status Push( SqStack &S, SElemType e ){ if(S.top-S.base >= S.stacksize) {

S.base=(SElemType *)realloc(S.base,(S.stacksize +INCREMENT)*sizeof(SElemType)); if(!S.base) exit(OVERFLOW); S.top = S.base+S.stacksize; S.stacksize += INCREMENT; } *(S.top)++ = e; return OK;}

Page 12: 第三章    栈和队列

55 、、退 栈退 栈

Status Pop(SqStack &S, SElemType &e)

{

if(S.top == S.base)

return ERROR;

e = *--S.top;

return OK;

}

Page 13: 第三章    栈和队列

66 、、栈 遍 历栈 遍 历

void StackTraverse( SqStack S ) {

SElemType *p;

p = S.base;

while( S.top > p )

printf (“%s”, *p++);

}

Page 14: 第三章    栈和队列

• 链 栈链 栈栈的链式存储结构称为链栈链栈。

栈顶

链栈的特点 ---( 1 )插入和删除(进栈 / 退

栈)仅在表头位置上(栈顶)进行。

( 2 )不需附加头结点,栈顶指针就是链表(即链栈)的头指针。

data link

栈底

Page 15: 第三章    栈和队列

栈的链接存储结构

链栈的结点定义 :

typedef char SElemType ;

typedef struct LinkNode{

SElemType data;

struct LinkNode *next;

} LinkNode, *LinkStack;

LinkStack top;

Page 16: 第三章    栈和队列

链栈的进栈算法 :

Status Push( LinkStack &top, SElemType e ){

LinkStack p;

p=(LinkNode *)malloc(sizeof(LinkNode));

if ( !p ) exit(ERROR);

p->data = e;

p->next=top;

top = p;

}

Page 17: 第三章    栈和队列

链栈的出栈算法 :Status Pop(LinkStack &top, SElemType &e){ if ( top == NULL) return ERROR; else { e = top->data; p= top; top = top->next; free(p); return OK; }}

Page 18: 第三章    栈和队列

3.2 3.2 栈的应用举例栈的应用举例

例 1 :括号匹配问题: 假设一个算术表达式中允许包含两种

括号:圆括号与方括号,其嵌套的次序随意。请设计一个算法判断一个算术表达式中的括号是否匹配。

Page 19: 第三章    栈和队列

void check() { SqStack s; SElemType ch[80], *p, e; if(InitStack(s)) // 初始化栈成功 { printf(" 请输入表达式 \n"); gets(ch); p = ch; while( *p ) // 没到串尾 switch(*p) { case '(': case '[': Push(s,*p++); break; // 左括号入栈,且 p++

Page 20: 第三章    栈和队列

case ')': case ']': if( !StackEmpty(s) ) { Pop(s,e); // 弹出栈顶元素 if(*p==')‘ && e!='(' || *p==']‘ && e!='[') { printf(" 左右括号不配对 \n"); exit(ERROR); } else{ p++; break; // 跳出 switch 语句 } } else { // 栈空 printf(" 缺乏左括号 \n"); exit(ERROR); }

Page 21: 第三章    栈和队列

default: p++; // 其它字符不处理,指针向后移 }

if(StackEmpty(s)) // 字符串结束时栈空 printf(" 括号匹配 \n");

else

printf(" 缺乏右括号 \n");

}

}

Page 22: 第三章    栈和队列

例 2 : 表达式求值问题: 设计一个算法,对给定的算术表达式

进行求值。 假定采用“算符优先法”对表达式进行

求值。- 一个表达式是由操作数、运算符、界限符组

成。- 任何两个相继出现的算符之间的优先级关系

为 > 、 = 、 < 。- 算符之间的优先级关系完全决定了操作数的

运算次序。

Page 23: 第三章    栈和队列

算符间的优先关系

+ - × / ( ) #

+ > > < < < > >

- > > < < < > >

× > > > > < > >

/ > > > > < > >

( < < < < < =

) > > > > > >

# < < < < < =

说明说明:• 左括号‘ ( ’ 的优先级最高• 当 θ1 = θ2 时,令

θ1 >θ2 。(左结合性)• 为了算法处理的方便,在表达式的最左和最右边各添加一个‘ # ’ 。•优先关系相等的情况只有‘ ( ’ = ‘ ) ’ 和‘ # ’ = ‘ # ’

θ1

θ2

Page 24: 第三章    栈和队列

表达式求值算法思想:表达式求值算法思想:

1 、首先将操作数栈 OPND 置为空栈,将表达式起始符“ #” 压入运算符栈 OPTR 中作为栈底元素。

2 、依次读入表达式的每个字符,直至当前读入字符与运算符栈的栈顶元素均为“ #” 时,结束下列循环 :• 若是操作数,则进操作数栈 OPND ;• 若是运算符 , 则进行以下判断 :- 若当前运算符高于 OPTR 栈顶运算符,将当前运算符

入栈 OPTR ,继续读入下一字符;- 若当前运算符低于 OPTR 栈顶运算符,栈顶运算符退

栈,并弹出操作数栈的两个栈顶操作数进行运算,再将运算结果压入操作数栈 OPND 中。- 若当前运算符等于栈顶运算符,表明栈顶运算符为“

(”,当前运算符为“)”,则栈顶运算符退栈。

为实现算符优先算法,需引入两个栈 OPTR 和OPND ,分别用于保存运算符与操作数。

Page 25: 第三章    栈和队列

SElemType EvaluateExpression() { SqStack OPTR,OPND; SElemType a,b,c,x,theta; InitStack(OPTR); InitStack(OPND); Push(OPTR,'#'); c = getchar(); GetTop(OPTR,x); while( c!='# ' || x!='# ' ){ if( In(c, OP) ) // c 是运算符 processing… else if(c>=‘0’&&c<=‘9’) { // c 是操作数 Push(OPND,c); c=getchar(); } else { // c 是非法字符 printf("ERROR\n"); exit(ERROR); } GetTop(OPTR,x); } GetTop(OPND,x); return x;

}

Page 26: 第三章    栈和队列

switch( Precede(x,c) ){ case‘ < < ‘ : // 栈顶元素优先权低 Push(OPTR,c); c=getchar(); break; case‘ = = ‘ : // 脱括号并接收下一字符 Pop(OPTR,x); c=getchar(); break; case ‘ > ‘ : // 退栈并将运算结果入栈

Pop(OPTR,theta); Pop(OPND,b);

Pop(OPND,a); Push(OPND,Operate(a,theta,b)); break; }

Page 27: 第三章    栈和队列

•定义定义–队列是只允许在一端删除,在另一端插队列是只允许在一端删除,在另一端插入的线性表。入的线性表。

–队头队头 ((frontfront)) : 指允许删除的一端 。: 指允许删除的一端 。–队尾队尾 ((rearrear)) :允许插入的一端。:允许插入的一端。

•特性特性–先进先出先进先出 ((FIFOFIFO, , First In First OutFirst In First Out))

3.3 3.3 队列 队列 (( Queue )Queue )

Page 28: 第三章    栈和队列

队列的基本运算:

入队 ( EnQueue )

出队 (DeQueue )

取队头元素 ( GetHead )

判队列是否为空 ( QueueEmpty )

置空队列 ( ClearQueue )

Page 29: 第三章    栈和队列

• 链队列—队列的链式表示与实现 链队列—队列的链式表示与实现 队列的链式存储结构简称为链队列,它

是限制仅在表头删除和表尾插入的单链表。

显然仅有单链表的头指针不便于在表尾做插入操作,为此再增加一个尾指针,指向链表的最后一个结点。

Page 30: 第三章    栈和队列

链队列的类型定义 链队列的类型定义

typedef struct QNode{

QElemType data;

struct QNode *next;

} QNode *QueuePtrQueuePtr;

typedef struct{

QueuePtr front;

QueuePtr rear;

}LinkQueueLinkQueue;

Page 31: 第三章    栈和队列

链队列是否需要头结点?– 为了操作的方便,在链队列中也添加一个头结

点,并使队头指针指向头结点。

如何判断队空?– 队空条件为 Q.front == Q.rear– 链队列在进队时无队满问题

Page 32: 第三章    栈和队列

// // 构造一个空队列构造一个空队列 QQ

Status InitQueue(LinkQueue &Q){

if( !(Q.front=Q.rear=(QueuePtr)malloc

(sizeof(QNode))));

exit(OVERFLOW);

Q.front->next=NULL;

return OK;

}

Page 33: 第三章    栈和队列

// // 入队 — 插入元素入队 — 插入元素 ee 为为 QQ 的新的队尾元的新的队尾元素素 Status EnQueue(LinkQueue &Q, QElemType e){

QueuePtr p;

if( !(p=(QueuePtr)malloc(sizeof(QNode))) )

exit(OVERFLOW);

p->data = e;

p->next = NULL;

Q.rear->next = p;

Q.rear = p;

return OK;

}

Page 34: 第三章    栈和队列

出队 — 若队列不空出队 — 若队列不空 ,, 删除删除 QQ 的队头元素的队头元素 ,, 用用 ee返回其值返回其值 ,,并返回并返回 OKOK,, 否则返回否则返回 ERRORERROR

Status DeQueue(LinkQueue &Q, QElemType &e) {

QueuePtr p; 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; }

Page 35: 第三章    栈和队列

// // 判 队 空判 队 空 Status QueueEmpty(LinkQueue Q) {

if(Q.front == Q.rear) return TRUE;

else return FALSE;

}

Status DestroyQueue( LinkQueue &Q ){

// // 销毁队列

while(Q.front) { Q.rear=Q.front->next; free(Q.front); Q.front=Q.rear; } return OK; }

Page 36: 第三章    栈和队列

2. 2. 循环队列——队列的顺序表示和实现循环队列——队列的顺序表示和实现

队列的顺序存储结构称为顺序队列,顺序队列实际上是运算受限的顺序表。

顺序队列也是用一个数组空间来存放当前队列中的元素。由于队列的队头和队尾的位置是变化的,因而要设两个指针和分别指示队头和队尾元素在队列中的位置。

Page 37: 第三章    栈和队列

顺序队列的类型定义顺序队列的类型定义 :: #define MAXQSIZE 5 typedef struct { QElemType *base; int front; int rear; } SqQueue ;

队头和队尾指针在队列初始化时均置为指向数组空间的下界 —0。 队头指针指向当前队头元素 队尾指针指向当前队尾元素的下一个位置

Page 38: 第三章    栈和队列

问题问题 11 :随着入队、出队操作的进行,队列中可能会出现什么现象?– “ 假上溢” 现象

“ 假上溢” 现象的解决方法:– 把队列看成是环状队列

问题问题 22 :在环状队列中如何判别“队满”与“队空”状态?– 另外引入一个标志变量以区别“队满”与“队空”状态。

– 牺牲一个元素空间的方法。 即入队前,测试尾指针在循环意义下加 1 后是

否等于头指针,若相等,则认为是队满。

Page 39: 第三章    栈和队列

// // 初始化空队列初始化空队列

Status InitQueue(SqQueue &Q){

Q.base=(QElemType *)malloc(MAXQSIZE

*sizeof(QElemType));

if(!Q.base) exit(OVERFLOW);

Q.front = Q.rear = 0;

return OK;

}

Page 40: 第三章    栈和队列

// // 入队算法入队算法

Status EnQueue(SqQueue &Q,QElemType e) {

if( (Q.rear+1)%MAXQSIZE == Q.front ) // 队列满

return ERROR;

Q.base[Q.rear] = e;

Q.rear=(Q.rear+1)%MAXQSIZE;

return OK;

}

Page 41: 第三章    栈和队列

// // 出队算法出队算法 Status DeQueue(SqQueue &Q, QElemType &e)

{

if(Q.front == Q.rear) // 队列空 return ERROR;

e = Q.base[Q.front];

Q.front = (Q.front+1)%MAXQSIZE;

return OK;

}

Page 42: 第三章    栈和队列

例 : 求迷宫的最短路径 .

01100110

00010001

10001010

11110000

01010101

11101000

1 2 3 4 5 6 7 8

1

2

3

4

5

6

3.43.4 队 列 应 用队 列 应 用

Page 43: 第三章    栈和队列

需要解决的问题 1 :如何从某一坐标点出发搜索其四周的邻点 ?

( ( x, yx, y ) )

(x-1, y) (x-1, y+1)

( x, y+1)

( x+1, y+1) ( x+1, y) ( x+1, y-1)

( x, y-1)

( x-1, y-1)x

y

+1-17

0-16

-1-15

-104

-1+13

0+12

+1+11

+100

yx

坐标增量数组 move

Page 44: 第三章    栈和队列

需要解决的问题 2 :如何存储搜索路径 ?

需要解决的问题 3 :如何防止重复到达某坐标点?

3……

2134

2333

1222

0111

preyx步

………

313

5

6

front

rear

Page 45: 第三章    栈和队列

需要解决的问题 4 :如何输出搜索路径 ?

1 … 12 13 14 15 16 17 18 19 20

x 1 … 5 2 5 6 5 6 6 5 6 …

y 1 … 6 6 3 1 7 5 4 8 8 …

pre 0 … 8 9 10 10 11 12 14 16 16 …

Page 46: 第三章    栈和队列

#define SIZE 64

#define M 10

#define N 10

int m=M-2, n=N-2;

 

typedef struct{

int x, y;

int pre;

} SqType;

SqType sq[SIZE];

struct moved{

int x, y;

} move[8];

 

int maze[M][N];

Page 47: 第三章    栈和队列

int ShortPath(int maze[][2]) { int i, j, v, front, rear, x, y; sq[1].x=1; sq[1].y=1; sq[1].pre=0; front=1; rear=1; maze[1][1]= -1; while( front <= rear ){ x=sq[front].x; y=sq[front].y; for (v=0;v<8;v++) { i = x+move[v].x; j = y+move[v].y; if ( maze[i],[j]==0 ){ rear++; sq[rear].x=i; sq[rear].y=j; sq[rear].pre=front; maze[i][j]=-1; }

Page 48: 第三章    栈和队列

if ( (i==m)&&(j==n) ){

PrintPath(sq,rear);

Restore(maze);

return 1;

}

}

front++;

}

return 0;

}

Page 49: 第三章    栈和队列

void PrintPath(SqType sq[], int rear)

{

int i;

i = rear;

do {

printf(“\n(%d,%d)”,sq[i].x,sq[i].y);

i=sq[i].pre;

} while ( i != 0 );

}

Page 50: 第三章    栈和队列