1 / 72

# 第三章 栈和队列 - PowerPoint PPT Presentation

I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.

## PowerPoint Slideshow about '第三章 栈和队列' - flann

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

3.1 栈

3.2 栈的应用举例

3.3 栈与递归的实现

3.4 队列

3.1.1 栈的定义及基本运算

3.1 栈(Stack)

D={ai |ai ElemSet, i=1,2,…,n (n>=0)}

InitStack(&S)

DestroyStack(&S)

ClearStack(&S)

StackEmpty(S)

StackLength(S)

GetTop(S,&e) //读栈顶元素

Push(&S,e) //入栈

Pop(&S,&e) //出栈

StackTraverse(S,visit())

3.1.2 栈的表示和实现

• 顺序栈
• 链栈

#define STACK_INIT_SIZE 100

#define STACKINCREMENT 10

typedef struct {

SElemType *base;

//栈底指针，base=NULL表明栈不存在

SElemType *top;

//栈顶指针,top==base为栈空的标志

int stacksize; //栈当前可以使用的最大容量

}SqStack;

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

base

base

base

1

1

1

top

0

0

0

F

E

D

C

B

A

top==base,栈空，此时出栈，则下溢（underflow)

top== stacksize,栈满，此时入栈，则上溢（overflow)

0

M-1

• 在一个程序中同时使用两个栈

1、构造一个空栈

Status InitSatck(SqStack &S) { S.base=(SElemType*)malloc(STACK_INIT_SIZE *sizeof(SElemType));

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

S.top=S.base;

S.stacksize= STACK_INIT_SIZE;

}//end InitSatck

2、返回栈顶元素

Status GetTop(SqStack S，SElemTtype &e) {

if (S.top==S.base) return ERROR;

e=*(S.top-1);

return OK;

}//end GetTop

3、入栈

Status Push(SqStack &S，SElemType e) {

if(S.top-S.base>=S.stacksize){

S.base=(SElemType*)realloc(S.base,(S. stacksize +STACKINCREMENT)* sizeof(SElemType));

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

S.top=S.base+S.stacksize;

S.stacksize+= STACKINCREMENT; }

*S.top++=e;

//end Push

4、出栈

Status Pop(SqStack &S，SElemType &e) {

if(S.top==S.base) return ERROR;

e=*--S.top;

return OK;

}//end Pop

top

…...

next

data

^

3.2 栈的应用举例

3.2.1 数制转换

3.2.2 括号匹配的检验

3.2.3 行编辑程序

3.2.5 表达式求值

n=(n div d)*d + n mod d

( 其中:div为整除运算,mod为求余运算)

3.2.1 数制转换

n n div 8 n mod 81348 168 4

168 21 0 21 2 5 2 0 2

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);

}

} //end conversion

{ [ ( ) ( ) ] }

1 2 3 4 5 6 7 8

3.2.2 括号匹配的检验

1）凡出现左括号，则入栈；

2）凡出现右括号，首先检查栈是否空.

3）表达式检验结束时，

Status matching(string &exp) {

int state = 1; InitStack(S);

while (i<=StrLength(exp) && state)

switch of exp[i] {

case “(”:{ Push(S,exp[i]); i++; break;}

case “)”: {

if(!StackEmpty(S) && GetTop(S)=“(”)

{ Pop(S,e); i++;}

else { state = 0;}

break; }

if (StackEmpty(S) && state) return OK;}

3.2.3 行编辑程序

whli##ilr#e（s#*s)

outcha@putchar(*s=#++);

while (*s)

putchar(*s++);

void LineEdit( ) {

InitStack(S); //栈S设为输入缓冲区

ch=getchar( );

while(ch!=eof){

while(ch!=eof && ch!=‘\n’){

switch(ch){

case ‘#’ : Pop(S,ch); //书上为c

case ‘@’ : ClearStack(S);

default : Push(S,ch);

}//end switch

ch=getchar( );

}//end while

ClearStack(s);

if(ch!=eof) ch=gethar( );

} //end while

DestroyStack(s);

}//end LineEdit

3.2.5 表达式求值

（1）算术四则运算的规则：

a.从左算到右

b.先乘除，后加减

c.先括号内，后括号外

3*（7 – 2 ）= 3 * 5 = 15

3.2.5 表达式求值

（2）根据上述三条运算规则，在运算的每一步中，对任意相继出现的算符1和2 ，都要比较优先权关系。

‘ # ’==‘ # ’ 表明表达式求值完毕。

3.2.5 表达式求值

（3）算法思想：

• 设两个栈：操作符栈 OPTR ，操作数栈 OPND
• 栈初始化：设操作数栈 OPND 为空；操作符栈 OPTR 的栈底
• 元素为表达式起始符 ‘#’；
• 依次读入字符：是操作数则入OPND栈，是操作符则要判断：

if操作符(1)< 栈顶元素(2)，则出栈、计算，结果压入OPND栈

OPTR

OPND

INPUT

OPERATE

#

3*(7-2)#

Push(opnd,’3’)

#

3

*(7-2)#

Push(optr,’*’)

#, *

3

(7-2)#

Push(optr,’(’)

#, *, (

3

7-2)#

Push(opnd,’7’)

#, *, (

3,7

-2)#

Push(optr,’-’)

#, *, (, －

3,7

2)#

Push(opnd,’2’)

)#

#, *, (, －

3,7,2

Operate(7-2)

#, *, (

3,5

)#

Pop(optr)

#, *

3,5

#

Operate(3*5)

#

15

#

GetTop(opnd)

Status EvaluateExpression( OperandType &result) {

InitStack(OPND); InitStack(OPTR); Push(OPTR ,’#’);

c=getchar( );

while((c!=‘#’)&&(GetTop(OPTR)!=‘#’))

{ if (!In(c,OP) { Push(OPND,c); c=getchar( );}

else switch(compare(c,GetTop(OPTR))) {

case ‘>’ : Push(OPTR , c); c=getchar( ); break;

case ‘=’: Pop(OPTR,x); c=getchar( ); break;

case ‘<’ : Pop(OPTR,tem); Pop(OPND,b ); a=Pop(OPND,a );

result=Operate(a,tem,b); Push(OPND,result);

c=getchar( ); break;

} //switch

}//while

result=GetTop(OPND);}//End EvaluateExpression

3.2.4 迷宫求解

• 若当前位置“可通”，则纳入路径，继续前进;
• 若当前位置“不可通”，则后退，换方向继续探索;
• 若四周“均无通路”，则将当前位置从路径中删除出去。

do{

if(当前位置可通){

if(该位置是出口位置) 结束

else 切换当前位置的东邻方块为新的当前位置;

}

else{ if(栈不空栈顶位置尚有其它方向未经探索)

if(栈不空且栈顶位置的四周均不通){

}

}

}while(栈不空);

Type struct{

int ord; //通道块在路径上的序号

PosType seat; //通道块在迷宫中的“坐标位置”

int di; //从此通道块走向下一通道块的方向

}SElemType; //栈元素类型

Status MazePath(MazeType maze, PosType start, PosType end) {

InitStack(S); curpos=start; //当前位置为入口位置

curstep=1; //探索第一步

do{ if(Pass(curpos)){ //当前位置可以通过

footprint(curpos); //留下足印

e=(curstep,curpos,1); Push(S,e); //加入路径

if(curpos==end) return(TRUE); //到达终点

curpose=NextPos(curpos,1);

//下一个位置是当前位置的东邻

curstep++;

else{ //当前位置不通

if(!StackEmpty(S)){

Pop(S,e);

while(e.di==4 && !StackEmpty(S)){

MarkPrint(e.seat); Pop(S,e);//留下不能通过的标记并后退

}//end while

if(e.di<4){

e.di++; Push(S,e); //换下一个方向探索

curpos=NextPos(e.seat, e.di);//新位置是旧位置的相邻块

}//end if

} //end if

}//end else

}while(!StackEmpty(S));

return(FALSE);

}//end MazePath

t

s

s

r

r

r

s

t

r

r

s

r

3.3 栈与递归的实现

3.3 栈与递归的实现

• 二、递归函数及其实现
• 递归：函数直接或间接的调用自身
• 实现：建立递归工作栈

3.3 栈与递归的实现

int fact (int w)

{ if ( w==0)

fact= 1;

else

fact=w* fact ( w-1);

}

main()

{ int f;

f=fact(3);

print(f);

}

3.3 栈与递归的实现

• 1.定义出口
• 2.把一个大的问题划分成同类型的子问题
• 3.解的合成

int fact (int w)

{1if ( w==0)

2 fact= 1;

3else

4fact=w* fact( w-1);

}

top

4 w=3

int fact (int w)

{1if ( w==0)

2 fact= 1;

3else

4fact=w* fact( w-1);

}

w

3

3*fact(2);

top

4 w=2

4 w=3

w

int fact (int w)

{1if ( w==0)

2 fact= 1;

3else

4fact=w* fact( w-1);

}

w

2

3

2*fact(1);

3*fact(2);

top

4 w=1

4 w=2

4 w=3

w

1

w

int fact (int w)

{1if ( w==0)

2 fact= 1;

3else

4fact=w* fact( w-1);

}

1*fact(0);

w

2

3

2*fact(1);

3*fact(2);

top

w

0

fact(0)=1

4 w=1

4 w=2

4 w=3

w

1

w

int fact (int w)

{1if ( w==0)

2 fact= 1;

3else

4fact=w* fact( w-1);

}

1*fact(0);

w

2

3

2*fact(1);

3*fact(2);

top

w

0

fact(0)=1

4 w=2

4 w=3

w

w

2

1

int fact (int w)

{1if ( w==0)

2 fact= 1;

3else

4fact=w* fact( w-1);

}

w

1*fact(0);

3

2*fact(1);

3*fact(2);

fact(1)=1

top

w

0

fact(0)=1

4 w=3

w

w

2

1

int fact (int w)

{1if ( w==0)

2 fact= 1;

3else

4fact=w* fact( w-1);

}

w

2*fact(1);

1*fact(0);

3

fact(2)=2

3*fact(2);

fact(1)=1

top

w

0

fact(0)=1

w

w

2

1

int fact (int w)

{1if ( w==0)

2 fact= 1;

3else

4fact=w* fact( w-1);

}

w

2*fact(1);

1*fact(0);

3

fact(2)=2

3*fact(2);

fact(1)=1

fact(3)=6

top

• 每次只能移一个圆盘
• 圆盘可在三个塔座上任意移动
• 任何时刻，每个塔座上不能将大盘压到

n x y z 返回地址

• 解决方法
• 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和返回地址
• 返回地址用行编号表示

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",&n);

printf(" Steps : %3d disks ",n);

hanoi(n,‘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) }

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 n;

printf("Input the number of disks

scanf("%d",&n);

printf("The steps to moving %3d

hanoi(n,'X','Y',‘Z');

(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) }

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 n;

printf("Input the number of disks

scanf("%d",&n);

printf("The steps to moving %3d

hanoi(n,'X','Y','Z');

(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) }

A

B

C

A

B

C

3 A B C 0

2 B A C 8

1 A B C 8

3 A B C 0

2 B A C 8

3 A B C 0

3 A B C 0

2 B A C 8

main()

{ int n;

printf("Input the number of disks

scanf("%d",&n);

printf("The steps to moving %3d

hanoi(n,'X','Y','Z');

(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) }

3.4 队列

3.4.1 抽象数据类型队列的定义

• 队列的定义及特点
• 定义：队列是限定只能在表的一端进行插入，

• 队尾(rear)——允许插入的一端
• 队头(front)——允许删除的一端
• 队列特点：先进先出(FIFO)

D={ ai|ai QElemSet, i=1,2,…,n ,n>=0}

R={<ai-1,ai>|ai-1,ai D,i=2,…,n}

InitQueue(&Q)

DestroyQueue(&Q)

ClearQueue(&Q)

QueueEmpty(Q)

QueueLength(Q)

EnQueue(&Q,e)//入队

DeQueue(&Q,&e) //出队

QueueTraverse(Q,visit())

3.4.2 链队列

typedef struct{

QueuePtr front;

QueuePtr rear;

typedef struct QNode{

QElemType data;

struct QNode *next;

}QNode,*QueuePtr;

1.构造一个空队列

{

Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode));

//if(!Q.front) exit(OVERFLOW);

Q.front->next=NULL;

}//end InitQueue

2.销毁队列

{ while(Q.front){

Q.rear=Q.front->next;

free(Q.front);

Q.front=Q.rear;

}//end while

}//end DestoryQueue

3.入队

p=(QueuePtr )malloc(sizeof(QNode));

p–>data=e;

p–>next=null;

Q.rear->next=p; Q.rear=p;

}// end EnQueue

4. 出队

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);

}//end DeQueue

rear

front

J6

J5

5

5

5

5

rear

front

J4

4

4

4

4

rear

rear

rear

J3

3

3

3

3

front

front

front

J2

2

2

2

2

front=0

rear=0

rear

J1

1

1

1

1

front

J1,J2,J3出队

J4,J5,J6入队

J1,J2,J3入队

0

0

0

0

3.4.3 循环队列的顺序存储结构和实现

（用一维数组实现sq[M]）

J3

J2

J1

front指示队头元素；

rear指示队尾元素的下一个位置；

M-1

rear

…...

0

1

…...

front

• 存在问题

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

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

1.另外设一个标志以区别队空、队满

2.少用一个元素空间

#define QueueSize 100

typedef Struct{

int front;

int rear;

QElemType *base; 或QElemType data[QueueSize];

}SqQueue;

1. 构造空队列

void InitQueue(SqQueue &Q){

Q.base=(QElemType*)malloc(QueueSize*sizeof(QElemType));

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

Q.front=Q.rear=0;

}

2. 返回队列长度

int QueueLength(SqQueue Q){

Return (Q.rear-Q.front+ QueueSize )% QueueSize;

}

3. 入队

Status EnQueue(SqQueue &Q,QElemType e){

if((Q.rear+1)% QueueSize ==Q.front )

return ERROR;

Q.base[Q.rear]=e;

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

return OK;

}

4. 出队

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

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

e=Q.base[Q.front];

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

return OK;

}

• 理解栈和队列这两种抽象数据类型的特点及与线性表的异同
• 熟悉顺序栈和链栈的组织方法以及基本运算的实现
• 理解递归算法
• 掌握链队列和循环队列的组织方法、简单算法实现
• 队满、队空的判断条件及其描述

p21: 3.1题, p22: 3.4题

p23: 3.12题，p24: 3.13题

p25: 3.29题