79
通通通 通通通通通通通 通通通通通通通通通通“通 通”通通通通通通通通 , i, x) Insert(S, n+1, x) Insert( L, i) Delete(S, n) De 通通通通通通通通通通通通通通

第三章 栈和队列(新)

Embed Size (px)

Citation preview

Page 1: 第三章 栈和队列(新)

通常称,栈和队列是限定插入和删除只能在表的“端点”进行的线性表。

线性表 栈 队列Insert(L, i, x) Insert(S, n+1, x) Insert(Q, n+1, x) 1≤i≤n+1 Delete(L, i) Delete(S, n) Delete(Q, 1) 1≤i≤n

栈和队列是两种常用的数据类型

Page 2: 第三章 栈和队列(新)

第三章 栈和队列3.1 栈 (stack)

3.2 栈的应用举例3.4 队列(Queue)

Page 3: 第三章 栈和队列(新)

学习提要:1.掌握栈和队列这两种抽象数据类型的特点, 并能在相应的应用问题中正确选用它们。2.熟练掌握栈类型的两种实现方法,即两种存 储结构表示时的基本操作实现算法,特别应 注意栈满和栈空的条件以及它们的描述方法。3.熟练掌握循环队列和链队列的基本操作实现 算法,特别注意队满和队空的描述方法。重难点内容: 顺序栈的相关操作、循环队列的判空判满

Page 4: 第三章 栈和队列(新)

§3.1 栈( stack )

3.1.1 栈的类型定义

3.1.2 栈的表示和实现

Page 5: 第三章 栈和队列(新)

栈的定义和特点定义:限定仅在表尾进行插入或删除操作的线性表,表尾—栈顶,表头—栈底,不含元素的空表称空栈。

an

a1

a2…

….

..栈底

栈顶

... 出栈进栈

栈 s=(a1,a2,……,an)

特点:先进后出( FILO )或后进先出( LIFO )

3.1.1 栈的类型定义

Page 6: 第三章 栈和队列(新)

ADT Stack {

数据对象: D = { ai | ai ElemSet, i=1,2,...,n, n≥0 }∈

数据关系: R1 = { <ai-1, ai >| ai-1, ai D, i=2,...,n }∈

约定 an 端为栈顶, a1 端为栈底。 基本操作:

} ADT Stack

栈的类型定义

Page 7: 第三章 栈和队列(新)

InitStack(&S)DestroyStack(&S)

ClearStack(&S)

StackEmpty(s)StackLength(S)

GetTop(S, &e)

Push(&S, e)Pop(&S, &e)

StackTravers(S, visit())

Page 8: 第三章 栈和队列(新)

顺序栈3.1.2 栈的表示和实现

类似于线性表的顺序映象实现,指向表尾的指针可以作为栈顶指针。//----- 栈的顺序存储表示 -----

#define STACK_INIT_SIZE 100; // 存储空间初始分配量 #define STACKINCREMENT 10;// 存储空间分配增量 typedef struct { SElemType *base; // 栈底指针 SElemType *top; // 栈顶指针 int stacksize; // 栈的当前可使用的最大容量 } SqStack;

Page 9: 第三章 栈和队列(新)

实现:一维数组 s[M]

top12345

0进栈

A

栈满

BCDEF

设数组维数为 Mtop=base,栈空,此时出栈,

则下溢( underflow)top=M,栈满,此时入栈,则上

溢( overflow)

toptoptoptoptop

12345

0

空栈topbase base

top出栈

toptop

栈空

base

栈底指针 base,始终指向栈底位置;栈顶指针 top,其初值指向栈底,始终在栈顶元素的下一个位置

12345

0AB

top

Page 10: 第三章 栈和队列(新)

Status InitStack (SqStack &S){// 构造一个空栈 S

S.base=(SElemType*)malloc(STACK_INIT_SIZ

E *sizeof(SElemType));

if (!S.base) exit (OVERFLOW); // 存储分配失败 S.top = S.base;

S.stacksize = STACK_INIT_SIZE;

return OK;}

Page 11: 第三章 栈和队列(新)

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; return OK; }

Page 12: 第三章 栈和队列(新)

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

// 若栈不空,则删除 S 的栈顶元素, // 用 e 返回其值,并返回 OK ; // 否则返回 ERROR

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

e = *--S.top;

return OK;

}

Page 13: 第三章 栈和队列(新)

链栈

栈的链式存储结构。栈顶指针就是链表的头指针。

栈顶指针∧a1an

注意 : 链栈中指针的方向注意 : 链栈中指针的方向

an-1

Page 14: 第三章 栈和队列(新)

入栈操作

出栈操作

^…...栈底

toptop

xp

top

^…...栈底

top

q

p->next=top ; top=p

q=top ; top=top->next; //q返回了出栈的元素

Page 15: 第三章 栈和队列(新)

§3.2 栈的应用3.2.1 数制转换3.2.2 括号匹配的检验3.2.3 行编辑程序问题3.2.4 迷宫求解3.2.5 表达式求值

Page 16: 第三章 栈和队列(新)

3.2.1 数制转换

十进制 N 和其他 d 进制数的转换原理 :

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

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

Page 17: 第三章 栈和队列(新)

toptop

4

top

40

top

405

例如: (1348)10=(2504)8 ,其运算过程如下:

N N div 8 N mod 8

1348 168 4

168 21 0

21 2 5

2 0 2

计算顺序

输出顺序

top

4052

Page 18: 第三章 栈和队列(新)

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

Page 19: 第三章 栈和队列(新)

3.3.2 括号匹配的检验

则 检验括号是否匹配的方法可用“期待的急迫程度”这个概念来描述。

假设在表达式中([]())或[([ ][ ])]等为正确的格式,[( ])或([( ))或 (()] )

均为不正确的格式。

Page 20: 第三章 栈和队列(新)

分析可能出现的不匹配的情况 :

到来的右括弧并非是所“期待”的;

例如:考虑下列括号序列: [ ( [ ] [ ] ) ] 1 2 3 4 5 6 7 8

直到结束,也没有到来所“期待”的括弧。

Page 21: 第三章 栈和队列(新)

算法的设计思想:1 )凡出现左括弧,则进栈;2 )凡出现右括弧,首先检查栈是否空 若栈空,则表明该“右括弧”多余, 否则和栈顶元素比较, 若相匹配,则“左括弧出栈” , 否则表明不匹配。3 )表达式检验结束时, 若栈空,则表明表达式中匹配正确, 否则表明“左括弧”有余。

Page 22: 第三章 栈和队列(新)

3.2.3 行编辑程序问题如何实现?

“每接受一个字符即存入存储器” ?

不恰当 !

合理的作法是: 设立一个输入缓冲区,用以接受用户输入的一行字符,然后逐行存入用户数据区,并假设“ #” 为退格符,“ @” 为退行符。

Page 23: 第三章 栈和队列(新)

假设从终端接受了这样两行字符: whli##ilr#e ( s#*s)

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

则实际有效的是下列两行: while (*s)

putchar(*s++);

Page 24: 第三章 栈和队列(新)

whli##ilr#e ( s#*s)

l

h

w

#

h

w

#

h

wwhli i

l i

lr

l

i

h

w

#

r

Page 25: 第三章 栈和队列(新)

whli##ilr#e ( s#*s)

l

i

h

w

e

(

e

l

i

h

w

(s

#

s

(

e

l

i

h

w

s

*

)

s

*

(

e

l

i

h

w

EOF

)

Page 26: 第三章 栈和队列(新)

Void LineEdit( ){// 利用字符栈 S, 从终端接收一行并传送至调用过程的数据区

InitStack(S); // 构造空栈ch = getchar(); // 从终端接受一个字符While(ch!= EOF){ //EOF 为全文结束符... // 对输入的字符的判断与处理

ClearStack(S); // 重置 S 为空栈if (ch != EOF) ch = getchar();}DestroyStack(S);} //LineEdid

Page 27: 第三章 栈和队列(新)

while (ch != EOF && ch != ‘\n’) { // ‘\n’ 为换行符 switch (ch) { case '#' : Pop(S, c); break; case '@': ClearStack(S); break;// 重置 S 为空栈 default : Push(S, ch); break; } ch = getchar(); // 从终端接收下一个字符 }

while (ch != EOF) { //EOF 为全文结束符

将从栈底到栈顶的字符传送至调用过程的数据区;

Page 28: 第三章 栈和队列(新)

3.2.4 迷宫求解通常用的是“穷举求解”的方法

# # # # # # # # # ## # $ $ $ # ## # $ $ $ # ## $ $ # # ## # # # # ## # # ## # # ## # # # # # # ## ## # # # # # # # # #

Page 29: 第三章 栈和队列(新)

求迷宫路径算法的基本思想是:

若当前位置“可通”,则纳入路径,继续前进 ;

若当前位置“不可通”,则后退,换方向继续探索 ;

若四周“均无通路”,则将当前位 置从路径中删除出去。

Page 30: 第三章 栈和队列(新)

“下一个位置”指“当前位置”四周“东南西北”四个方向上相临的通道块。

西 东

南当前位置

Page 31: 第三章 栈和队列(新)

根据算法思想 , 为了保证在任何位置上都能沿原路退回 ,需要用一个后进先出的结构来保存从入口到当前位置的路径 .

假设以栈 S记录“当前路径”,则栈顶中存放的是“当前路径上最后一个通道块”。由此,“纳入路径”的操作即为“当前位置入栈”;“从当前路径上删除前一通道块”的操作为“出栈”。

Page 32: 第三章 栈和队列(新)

算法描述设定当前位置的初值为入口位置do {

若当前位置可通 则 { 将当前位置插入栈顶 ; //纳入路径 若该位置是出口位置 , 则结束 ; // 求得路径存放

在栈中 否则切换当前位置的东相邻方块为新的当前位

置 ;

}

当前位置

出口

当前位置

当前位置

Page 33: 第三章 栈和队列(新)

算法描述否则 // 不通若栈不空且栈顶位置尚有其他方向未探索 ,

则设定新的当前位置为沿顺时针方向旋转找的栈顶位置的下一相邻块 ;

当前位置栈顶位置

Page 34: 第三章 栈和队列(新)

算法描述若栈不空但栈顶位置的四周均不可通则 { 删去栈顶位置 ;

若栈不空 , 则重新测试新的栈顶位置 ,

直到找到一个可通的相邻块或出栈至栈空 ;

}

}while( 栈不空 );当前位置

Page 35: 第三章 栈和队列(新)

算法描述说明 : 当前位置可通 , 指未曾走到过的通道块 , 即要求该通道块既不在当前路径上 , 也不是曾经纳入过路径的通道块 .

起点

终点

d

ab

f

g

a-b-c-f-g ( 简单路径 )

a-b-c-d-e-c-f-g ( 非简单路径 )c

e 若 b 已经纳入过路径 , 则在当前位置为 f 时 , 将不作为可通位置考虑 , 否则 , 将形成死循环 .

Page 36: 第三章 栈和队列(新)

通道块的数据描述Type struct {int ord; // 通道块在路径上的“序号”PosType seat; // 通道块在迷宫中的“坐标”

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

} SElemType; // 栈的元素类型

Page 37: 第三章 栈和队列(新)

迷宫问题算法Status MazePath(MazeType maze,PosType start, PosType end){

// 若迷宫 maze 中存在从 start 到 end 的通道,则求得一条存放在栈中,并返回 true ;否则返回 false

InitStack(S); curpos = start; // 设定当前位置为“入口位置”curstep = 1; //探索第一步do {

...

} while (!StackEmpty(s));

return (false);

} //MazePath

Page 38: 第三章 栈和队列(新)

迷宫问题算法do {

if (Pass(curpos)) { // 当前位置可通 FootPrint(curpos); //留下足迹 e = ( curstep,curpos,1);

Push(S,e); // 入栈,加入足迹 if (curpos == end) return (ture); // 到达出口 curpos = NextPos(curpos,1);

// 下一个位置为当前位置的东邻 curstep++; //探索下一个位置 }// if

...

Page 39: 第三章 栈和队列(新)

迷宫问题算法else { // 当前位置不能通过If ( !StackEmpty(s)) {

Pop(S,e) ; // 出栈,弹出当前的栈顶位置至 e

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

MarkPrint(e.seat); Pop(S,e); // 若该位置无方向可寻,留下不能通过的标记,并退回一步 } //while

if (e.di < 4) { // 表示仍有方向可寻 e.di++; Push(S,e); // 换下一方向试探 curpos = NextPos(e.seat, e.di);

} //end if

}//end else

Page 40: 第三章 栈和队列(新)

迷宫问题演示

成功 不成功

Page 41: 第三章 栈和队列(新)

限于二元运算符的表达式定义 : Exp = S1 OP S2 操作数 : 变量、常量、表达式 运算符 : 算术运算符、关系运算符、 逻辑运算符 界限符:括号、结束符

3.2.5 表达式求值

Page 42: 第三章 栈和队列(新)

算法思想: 设置两个工作栈, OPTR 存运算符, OPND 存操作数及运算结果。算法运算思想:

1. 首先置 OPND 栈为空,“ #” 为运算符栈底元素。 2.依次读入表达式中的每个字符,若是操作数进 OPND 栈 , 若是运算符则和 OPTR栈顶元素比较优先权后作相应操作,直至整个表达式求值完毕。

Page 43: 第三章 栈和队列(新)

例: 3 * ( 7 – 2 )

OPND 栈 OPTR 栈

C CC

3

*

(

C

7

C

C

2

C

- 275

C

* 5315

Page 44: 第三章 栈和队列(新)

例: 3 * ( 7 – 2 ) OPTR 栈 OPND 栈 输入 操作1 # 3 * ( 7 – 2 ) # PUSH( OPND, ‘3’ )

2 # 3 * ( 7 – 2 ) # PUSH( OPTR, ‘*’ )

3 # * 3 ( 7 – 2 ) # PUSH( OPTR, ‘(’ )

4 # * ( 3 7 – 2 ) # PUSH( OPND, ‘7’ )

5 # * ( 3 7 – 2 ) # PUSH( OPTR, ‘–’ )

6 # * (– 3 7 2 ) # PHSH( OPND, ‘2’ )

7 # * (– 3 7 2 ) # operate( ‘7’,’-’,’2’ )

8 # * ( 3 5 ) # POP( OPTR )

9 # * 3 5 # operate( ‘3’, ‘*’, ‘5’ )

10 # 15 # return GetTop( OPND )

“(” < “*” , 因此“ ( ” 入操作符栈 OPTR

Page 45: 第三章 栈和队列(新)

例: 3 * ( 7 – 2 ) OPTR 栈 OPND 栈 输入 操作1 # 3 * ( 7 – 2 ) # PUSH( OPND, ‘3’ )

2 # 3 * ( 7 – 2 ) # PUSH( OPTR, ‘*’ )

3 # * 3 ( 7 – 2 ) # PUSH( OPTR, ‘(’ )

4 # * ( 3 7 – 2 ) # PUSH( OPND, ‘7’ )

5 # * ( 3 7 – 2 ) # PUSH( OPTR, ‘–’ )

6 # * (– 3 7 2 ) # PHSH( OPND, ‘2’ )

7 # * (– 3 7 2 ) # operate( ‘7’,’-’,’2’ )

8 # * ( 3 5 ) # POP( OPTR )

9 # * 3 5 # operate( ‘3’, ‘*’, ‘5’ )

10 # 15 # return GetTop( OPND )

“-” >“ )” , 因此执行 7-2 操作,同时 OPTR栈弹出“ -” , OPND 栈弹出“ 2” 和“ 7”

Page 46: 第三章 栈和队列(新)

例: 3 * ( 7 – 2 ) OPTR 栈 OPND 栈 输入 操作1 # 3 * ( 7 – 2 ) # PUSH( OPND, ‘3’ )

2 # 3 * ( 7 – 2 ) # PUSH( OPTR, ‘*’ )

3 # * 3 ( 7 – 2 ) # PUSH( OPTR, ‘(’ )

4 # * ( 3 7 – 2 ) # PUSH( OPND, ‘7’ )

5 # * ( 3 7 – 2 ) # PUSH( OPTR, ‘–’ )

6 # * (– 3 7 2 ) # PHSH( OPND, ‘2’ )

7 # * (– 3 7 2 ) # operate( ‘7’,’-’,’2’ )

8 # * ( 3 5 ) # POP( OPTR )

9 # * 3 5 # operate( ‘3’, ‘*’, ‘5’ )

10 # 15 # return GetTop( OPND )

“)” =“(” , 操作符的优先权相等,弹出“ (”, 脱括号并接收下一字符 .

Page 47: 第三章 栈和队列(新)

OperandType EvaluateExpression() {

// 设 OPTR 和 OPND 分别为运算符栈和运算数栈, OP 为运算符集合。

InitStack (OPTR); Push (OPTR, '#');

initStack (OPND); c = getchar();

while (c!= '#' || GetTop(OPTR)!= '#') {

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

// 不是运算符则进栈 else

} // while

return GetTop(OPND);

} // EvaluateExpression

… … OP为运算符集合,若 c 不是运算符,则加入到运算数栈中

Page 48: 第三章 栈和队列(新)

switch ( precede(GetTop(OPTR), 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; } // switch

Page 49: 第三章 栈和队列(新)

算法演示( 3+5 ) * 2- 12 + 6 / 3 #

Page 50: 第三章 栈和队列(新)

第三章作业3.1 设将整数 1 、 2 、 3 、 4依次进栈,但只要出栈时栈非空,则可将出栈操作按任何次序夹入其中,请回答下列问题: ( 1 )若入栈次序为push(1) , pop() , push(2 ), push(3) , pop() ,pop( ) , push(4) , pop( ) ,则出栈的数字序列为什么? 3.2 写出检验括号匹配的算法。

Page 51: 第三章 栈和队列(新)

§3.4 队列3.4.1 队列的类型定义3.4.2 链队列3.4.3 循环队列

Page 52: 第三章 栈和队列(新)

队列是限定只能在表的一端进行插入,在表的另一端进行删除的线性表。

a1 a2 a3…………………….an 入队出队

front rear队列 Q=(a1,a2,……,an)

队列特点:先进先出 (FIFO)

3.4.1 队列的类型定义

队尾 (rear)——允许插入的一端队头 (front)——允许删除的一端

Page 53: 第三章 栈和队列(新)

ADT Queue { 数据对象: D = {ai | ai ElemSet, i=1,2,...,n, n≥0}∈

数据关系: R1 = { <a i-1,ai > | ai-1, ai D, i=2,...,n}∈

约定其中 a1 端为队列头, an 端为队列尾基本操作:

队列的类型定义

} ADT Queue

Page 54: 第三章 栈和队列(新)

队列的基本操作:

InitQueue(&Q) DestroyQueue(&Q)

QueueEmpty(Q) QueueLength(Q)

GetHead(Q, &e) ClearQueue(&Q)

DeQueue(&Q, &e)EnQueue(&Q, e)

QueueTravers(Q, visit())

Page 55: 第三章 栈和队列(新)

typedef struct QNode{// 结点类型 QElemType data ; struct QNode *next ; }QNode, *QueuePtr;

typedef struct{ // 链队列类型 QueuePtr front ; // 队头指针 QueuePtr rear ; // 队尾指针} LinkQueue;

3.4.2 链队列-队列的链式表示和实现

Page 56: 第三章 栈和队列(新)

a1∧an

…Q.frontQ.rear

Q.frontQ.rear

∧空队列

Page 57: 第三章 栈和队列(新)

front rear

x 入队 ^x

front rear

y 入队 x ^y

front rear

x 出队 x ^y

frontrear

空队 ^

frontrear

y 出队 ^

Q.rear -> next=p

Q.rear=p

p= Q.front -> next

Q.front -> next = p -> next

p

p

p

Page 58: 第三章 栈和队列(新)

Status InitQueue (LinkQueue &Q) { // 构造一个空队列 Q

Q.front = Q.rear =

(QueuePtr)malloc(sizeof(QNode));

if (!Q.front) exit (OVERFLOW); // 存储分配失败 Q.front->next = NULL;

return OK;}

Page 59: 第三章 栈和队列(新)

Status EnQueue (LinkQueue &Q,

QElemType e) {

// 插入元素 e 为 Q 的新的队尾元素 p = (QueuePtr) malloc (sizeof (QNode));

if (!p) exit (OVERFLOW); // 存储分配失败 p->data = e; p->next = NULL;

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

return OK;

}

Page 60: 第三章 栈和队列(新)

EnQueue

e

Q.front Q.rear

...

p

Q.rear

p->data = e; p->next = NULL;Q.rear->next = p; Q.rear = p;

Page 61: 第三章 栈和队列(新)

Status DeQueue (LinkQueue &Q,

QElemType &e) { // 若队列不空,则删除 Q 的队头元素, // 用 e 返回其值,并返回 OK ;否则返回ERROR

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 62: 第三章 栈和队列(新)

DeQueue

e

Q.front

...

p

Q.rear

p = Q.front->next; e = p->data;Q.front->next = p->next;if (Q.rear == p) Q.rear = Q.front;free (p);

Page 63: 第三章 栈和队列(新)

3.4.3 循环队列-队列的顺序表示和实现

#define MAXQSIZE 100 //最大队列长度 typedef struct {

QElemType *base; // 动态分配存储空间 int front; // 头指针,若队列不空, // 指向队列头元素 int rear; // 尾指针,若队列不空,指向 // 队列尾元素 的下一个位置 } SqQueue;

Page 64: 第三章 栈和队列(新)

实现:用一维数组实现 sq[M]

12345

0空队列

rear=0 front=0 J1

J2

J3

rear

rear

1

2

3

4

5

0J4,J5,J6 入队

J4J5J6

frontrear

rear

1

2

3

4

5

0front

J1,J1,J3 入队

rear

1

2

3

4

5

0

J1,J2,J3 出队J1

J2

J3

frontfrontfrontfront

存在问题:当 front=0,rear=M 时再有元素入队发生溢出——真溢出当 front≠0,rear=M 时再有元素入队发生溢出——假溢出

rear

Page 65: 第三章 栈和队列(新)

解决方案 1队首固定,每次出队剩余元素向下移动 ,避免发生假溢出 .

J6

J5

J4

J3

J2front

Rear

J6

J5

J4

J3front

Rear

J6

J5

J4

J3front

Rear

方法可行 , 但将花费太多的时间

Page 66: 第三章 栈和队列(新)

解决方案 2 循环队列 基本思想:把队列设想成环形,让 sq[0]接在 sq[M-1] 之后,若 rear+1==M, 则令rear=0;

J6

...

J5

J4

M-1

...

2

1

0

Rear

Front

J6

...

J5

J4

J7 Rear

Front

M-1

...

2

1

0

rear+1 = M, 于是令 rear = 0, “J7” 入队 ,有 sq[rear] = J7. 能够避免假溢出 .

Page 67: 第三章 栈和队列(新)

解决方案 2循环队列实现:利用“模”运算,实现若 rear+1==M,则 rear=0 的设定 ;

入队: sq[rear]=x; rear=(rear+1)%M;

出队: x=sq[front]; front=(front+1)%M;

队满、队空判定条件

Page 68: 第三章 栈和队列(新)

0123

4 5

rearfront

J5J6

J70123

4 5

rearfront

J4J9

J8

J4,J5,J6 出队J7,J8,J9 入队

队空: front==rear队满: front==rear

J5J6

0123

4 5

rear

front

初始状态

J4

队满队空如何区分判定 ?

Page 69: 第三章 栈和队列(新)

解决方案:1. 另外设一个标志位以区别队空、队满2. 少用一个元素空间: 队空: front==rear 队满: (rear+1)%M==front

3. 使用一个计数器记录队列中元素的总数当 count = Sq.length 时为队满 .

0123

4 5

rearfront

J5J6

J70123

4 5

rearfront

J4 J8

Page 70: 第三章 栈和队列(新)

分析可见 , 在 C语言中不能用动态分配的一维数组来实现循环队列 . 如果用户的应用程序中设有循环队列 , 则必须为它设定一个最大队列长度 ; 若用户无法预估所用队列的长度 ,

则宜用链队列 .

Page 71: 第三章 栈和队列(新)

Status InitQueue (SqQueue &Q) {

// 构造一个空队列 Q

Q.base = (QElemType *) malloc

(MAXQSIZE *sizeof (QElemType));

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

// 存储分配失败 Q.front = Q.rear = 0;

return OK;

}

Page 72: 第三章 栈和队列(新)

Status EnQueue (SqQueue &Q, QElemType

e) { // 插入元素 e 为 Q 的新的队尾元素 if ((Q.rear+1) % MAXQSIZE == Q.front)

return ERROR; // 队列满 Q.base[Q.rear] = e;

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

return OK;}

Page 73: 第三章 栈和队列(新)

Status DeQueue (SqQueue &Q, QElemType &e)

{ // 若队列不空,则删除 Q 的队头元素, // 用 e 返回其值,并返回 OK; 否则返回 ERROR

if (Q.front == Q.rear) return ERROR; // 队空 e = Q.base[Q.front];

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

return OK;

}

Page 74: 第三章 栈和队列(新)

例 . 写出以下程序段的输出结果(队列中的元素 类型 QElemType 为 char )。Void main( ){ Queue Q; InitQueue(Q); Char x=‘e’, y=‘c’; EnQueue(Q, ‘h’); EnQueue(Q, ‘r’); EnQueue(Q, y); DeQueue(Q, x); EnQueue(Q, x); DeQueue(Q, x); EnQueue(Q, ‘a’); While ( !QueueEmpty(Q) ){ DeQueue(Q, y); Printf(y); } Printf(x);}

Page 75: 第三章 栈和队列(新)

括号匹配的检验1 )凡出现左括弧,则进栈;2 )凡出现右括弧,首先检查栈是否空 若栈空,则表明该“右括弧”多余, 否则和栈顶元素比较, 若相匹配,则“左括弧出栈” , 否则表明不匹配。3 )表达式检验结束时, 若栈空,则表明表达式中匹配正确, 否则表明“左括弧”有余。

Page 76: 第三章 栈和队列(新)

括号匹配的检验Status check( ) { //对于输入的任意一个字符串 , 检验括号是否配对

SqStack s;

SElemType ch[80], *p,e; //ch存放字符串

InitStack(s); // 初始化栈成功

Gets(ch); // 输入字符串

p = ch; //p 指向字符串的首字符

... // 输入字符串中括号的检验

Page 77: 第三章 栈和队列(新)

括号匹配的检验while(*p) // 没有到串尾

Switch(*p){

case ‘(’, ‘[’ , ‘{’ :

Push(s,*p++); break; // 左括号入栈且 p++

case ‘)’, ‘]’ , ‘}’ :

if (!StackEmpty(s)) { // 若栈不空

Pop(s,e); //弹出栈顶元素

if (!(e==‘(’&& *p ==‘)’ ||

e==‘[’&& *p ==‘]’|| e==‘{’&& *p ==‘}’)//出现了 3 种匹配情况之外的情况

Page 78: 第三章 栈和队列(新)

括号匹配的检验{ printf(“ 左右括号不匹配 \n”);

return ERROR;

}

p++;

}

else {// 栈空

printf( “缺乏左括号 \n”);

return ERROR;

} //end if

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

} //end switch

} //end while

...

Page 79: 第三章 栈和队列(新)

括号匹配的检验if (StackEmpty(s)) // 字符串结束时栈空

printf(“ 括号匹配 \n”);

else printf(“缺少右括号 \n”);

}//end check