第三章 栈和队列(新)
TRANSCRIPT
通常称,栈和队列是限定插入和删除只能在表的“端点”进行的线性表。
线性表 栈 队列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
栈和队列是两种常用的数据类型
第三章 栈和队列3.1 栈 (stack)
3.2 栈的应用举例3.4 队列(Queue)
学习提要:1.掌握栈和队列这两种抽象数据类型的特点, 并能在相应的应用问题中正确选用它们。2.熟练掌握栈类型的两种实现方法,即两种存 储结构表示时的基本操作实现算法,特别应 注意栈满和栈空的条件以及它们的描述方法。3.熟练掌握循环队列和链队列的基本操作实现 算法,特别注意队满和队空的描述方法。重难点内容: 顺序栈的相关操作、循环队列的判空判满
§3.1 栈( stack )
3.1.1 栈的类型定义
3.1.2 栈的表示和实现
栈的定义和特点定义:限定仅在表尾进行插入或删除操作的线性表,表尾—栈顶,表头—栈底,不含元素的空表称空栈。
an
a1
a2…
….
..栈底
栈顶
... 出栈进栈
栈 s=(a1,a2,……,an)
特点:先进后出( FILO )或后进先出( LIFO )
3.1.1 栈的类型定义
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
栈的类型定义
InitStack(&S)DestroyStack(&S)
ClearStack(&S)
StackEmpty(s)StackLength(S)
GetTop(S, &e)
Push(&S, e)Pop(&S, &e)
StackTravers(S, visit())
顺序栈3.1.2 栈的表示和实现
类似于线性表的顺序映象实现,指向表尾的指针可以作为栈顶指针。//----- 栈的顺序存储表示 -----
#define STACK_INIT_SIZE 100; // 存储空间初始分配量 #define STACKINCREMENT 10;// 存储空间分配增量 typedef struct { SElemType *base; // 栈底指针 SElemType *top; // 栈顶指针 int stacksize; // 栈的当前可使用的最大容量 } SqStack;
实现:一维数组 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
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;}
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; }
Status Pop (SqStack &S, SElemType &e) {
// 若栈不空,则删除 S 的栈顶元素, // 用 e 返回其值,并返回 OK ; // 否则返回 ERROR
if (S.top == S.base) return ERROR;
e = *--S.top;
return OK;
}
链栈
栈的链式存储结构。栈顶指针就是链表的头指针。
栈顶指针∧a1an
注意 : 链栈中指针的方向注意 : 链栈中指针的方向
an-1
入栈操作
出栈操作
^…...栈底
toptop
xp
top
^…...栈底
top
q
p->next=top ; top=p
q=top ; top=top->next; //q返回了出栈的元素
§3.2 栈的应用3.2.1 数制转换3.2.2 括号匹配的检验3.2.3 行编辑程序问题3.2.4 迷宫求解3.2.5 表达式求值
3.2.1 数制转换
十进制 N 和其他 d 进制数的转换原理 :
N=( N div d )*d + N mod d
其中: div 为整除运算, mod 为求余运算
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
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
3.3.2 括号匹配的检验
则 检验括号是否匹配的方法可用“期待的急迫程度”这个概念来描述。
假设在表达式中([]())或[([ ][ ])]等为正确的格式,[( ])或([( ))或 (()] )
均为不正确的格式。
分析可能出现的不匹配的情况 :
到来的右括弧并非是所“期待”的;
例如:考虑下列括号序列: [ ( [ ] [ ] ) ] 1 2 3 4 5 6 7 8
直到结束,也没有到来所“期待”的括弧。
算法的设计思想:1 )凡出现左括弧,则进栈;2 )凡出现右括弧,首先检查栈是否空 若栈空,则表明该“右括弧”多余, 否则和栈顶元素比较, 若相匹配,则“左括弧出栈” , 否则表明不匹配。3 )表达式检验结束时, 若栈空,则表明表达式中匹配正确, 否则表明“左括弧”有余。
3.2.3 行编辑程序问题如何实现?
“每接受一个字符即存入存储器” ?
不恰当 !
合理的作法是: 设立一个输入缓冲区,用以接受用户输入的一行字符,然后逐行存入用户数据区,并假设“ #” 为退格符,“ @” 为退行符。
假设从终端接受了这样两行字符: whli##ilr#e ( s#*s)
outcha@putchar(*s=#++);
则实际有效的是下列两行: while (*s)
putchar(*s++);
whli##ilr#e ( s#*s)
l
h
w
#
h
w
#
h
wwhli i
l i
lr
l
i
h
w
#
r
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
)
Void LineEdit( ){// 利用字符栈 S, 从终端接收一行并传送至调用过程的数据区
InitStack(S); // 构造空栈ch = getchar(); // 从终端接受一个字符While(ch!= EOF){ //EOF 为全文结束符... // 对输入的字符的判断与处理
ClearStack(S); // 重置 S 为空栈if (ch != EOF) ch = getchar();}DestroyStack(S);} //LineEdid
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 为全文结束符
将从栈底到栈顶的字符传送至调用过程的数据区;
3.2.4 迷宫求解通常用的是“穷举求解”的方法
# # # # # # # # # ## # $ $ $ # ## # $ $ $ # ## $ $ # # ## # # # # ## # # ## # # ## # # # # # # ## ## # # # # # # # # #
求迷宫路径算法的基本思想是:
若当前位置“可通”,则纳入路径,继续前进 ;
若当前位置“不可通”,则后退,换方向继续探索 ;
若四周“均无通路”,则将当前位 置从路径中删除出去。
“下一个位置”指“当前位置”四周“东南西北”四个方向上相临的通道块。
西 东
北
南当前位置
根据算法思想 , 为了保证在任何位置上都能沿原路退回 ,需要用一个后进先出的结构来保存从入口到当前位置的路径 .
假设以栈 S记录“当前路径”,则栈顶中存放的是“当前路径上最后一个通道块”。由此,“纳入路径”的操作即为“当前位置入栈”;“从当前路径上删除前一通道块”的操作为“出栈”。
算法描述设定当前位置的初值为入口位置do {
若当前位置可通 则 { 将当前位置插入栈顶 ; //纳入路径 若该位置是出口位置 , 则结束 ; // 求得路径存放
在栈中 否则切换当前位置的东相邻方块为新的当前位
置 ;
}
当前位置
出口
当前位置
东
当前位置
算法描述否则 // 不通若栈不空且栈顶位置尚有其他方向未探索 ,
则设定新的当前位置为沿顺时针方向旋转找的栈顶位置的下一相邻块 ;
当前位置栈顶位置
算法描述若栈不空但栈顶位置的四周均不可通则 { 删去栈顶位置 ;
若栈不空 , 则重新测试新的栈顶位置 ,
直到找到一个可通的相邻块或出栈至栈空 ;
}
}while( 栈不空 );当前位置
算法描述说明 : 当前位置可通 , 指未曾走到过的通道块 , 即要求该通道块既不在当前路径上 , 也不是曾经纳入过路径的通道块 .
起点
终点
d
ab
f
g
a-b-c-f-g ( 简单路径 )
a-b-c-d-e-c-f-g ( 非简单路径 )c
e 若 b 已经纳入过路径 , 则在当前位置为 f 时 , 将不作为可通位置考虑 , 否则 , 将形成死循环 .
通道块的数据描述Type struct {int ord; // 通道块在路径上的“序号”PosType seat; // 通道块在迷宫中的“坐标”
int di; // 从此通道块走向下一通道块的“方向”
} SElemType; // 栈的元素类型
迷宫问题算法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
迷宫问题算法do {
if (Pass(curpos)) { // 当前位置可通 FootPrint(curpos); //留下足迹 e = ( curstep,curpos,1);
Push(S,e); // 入栈,加入足迹 if (curpos == end) return (ture); // 到达出口 curpos = NextPos(curpos,1);
// 下一个位置为当前位置的东邻 curstep++; //探索下一个位置 }// if
...
迷宫问题算法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
迷宫问题演示
成功 不成功
限于二元运算符的表达式定义 : Exp = S1 OP S2 操作数 : 变量、常量、表达式 运算符 : 算术运算符、关系运算符、 逻辑运算符 界限符:括号、结束符
3.2.5 表达式求值
算法思想: 设置两个工作栈, OPTR 存运算符, OPND 存操作数及运算结果。算法运算思想:
1. 首先置 OPND 栈为空,“ #” 为运算符栈底元素。 2.依次读入表达式中的每个字符,若是操作数进 OPND 栈 , 若是运算符则和 OPTR栈顶元素比较优先权后作相应操作,直至整个表达式求值完毕。
例: 3 * ( 7 – 2 )
OPND 栈 OPTR 栈
#
#
C CC
3
*
(
C
7
C
-
C
2
C
- 275
C
* 5315
例: 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
例: 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”
例: 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 )
“)” =“(” , 操作符的优先权相等,弹出“ (”, 脱括号并接收下一字符 .
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 不是运算符,则加入到运算数栈中
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
算法演示( 3+5 ) * 2- 12 + 6 / 3 #
第三章作业3.1 设将整数 1 、 2 、 3 、 4依次进栈,但只要出栈时栈非空,则可将出栈操作按任何次序夹入其中,请回答下列问题: ( 1 )若入栈次序为push(1) , pop() , push(2 ), push(3) , pop() ,pop( ) , push(4) , pop( ) ,则出栈的数字序列为什么? 3.2 写出检验括号匹配的算法。
§3.4 队列3.4.1 队列的类型定义3.4.2 链队列3.4.3 循环队列
队列是限定只能在表的一端进行插入,在表的另一端进行删除的线性表。
a1 a2 a3…………………….an 入队出队
front rear队列 Q=(a1,a2,……,an)
队列特点:先进先出 (FIFO)
3.4.1 队列的类型定义
队尾 (rear)——允许插入的一端队头 (front)——允许删除的一端
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
队列的基本操作:
InitQueue(&Q) DestroyQueue(&Q)
QueueEmpty(Q) QueueLength(Q)
GetHead(Q, &e) ClearQueue(&Q)
DeQueue(&Q, &e)EnQueue(&Q, e)
QueueTravers(Q, visit())
typedef struct QNode{// 结点类型 QElemType data ; struct QNode *next ; }QNode, *QueuePtr;
typedef struct{ // 链队列类型 QueuePtr front ; // 队头指针 QueuePtr rear ; // 队尾指针} LinkQueue;
3.4.2 链队列-队列的链式表示和实现
a1∧an
…Q.frontQ.rear
Q.frontQ.rear
∧空队列
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
Status InitQueue (LinkQueue &Q) { // 构造一个空队列 Q
Q.front = Q.rear =
(QueuePtr)malloc(sizeof(QNode));
if (!Q.front) exit (OVERFLOW); // 存储分配失败 Q.front->next = NULL;
return OK;}
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;
}
EnQueue
e
Q.front Q.rear
...
p
Q.rear
p->data = e; p->next = NULL;Q.rear->next = p; Q.rear = p;
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;
}
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);
3.4.3 循环队列-队列的顺序表示和实现
#define MAXQSIZE 100 //最大队列长度 typedef struct {
QElemType *base; // 动态分配存储空间 int front; // 头指针,若队列不空, // 指向队列头元素 int rear; // 尾指针,若队列不空,指向 // 队列尾元素 的下一个位置 } SqQueue;
实现:用一维数组实现 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
解决方案 1队首固定,每次出队剩余元素向下移动 ,避免发生假溢出 .
J6
J5
J4
J3
J2front
Rear
J6
J5
J4
J3front
Rear
J6
J5
J4
J3front
Rear
方法可行 , 但将花费太多的时间
解决方案 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. 能够避免假溢出 .
解决方案 2循环队列实现:利用“模”运算,实现若 rear+1==M,则 rear=0 的设定 ;
入队: sq[rear]=x; rear=(rear+1)%M;
出队: x=sq[front]; front=(front+1)%M;
队满、队空判定条件
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
队满队空如何区分判定 ?
解决方案:1. 另外设一个标志位以区别队空、队满2. 少用一个元素空间: 队空: front==rear 队满: (rear+1)%M==front
3. 使用一个计数器记录队列中元素的总数当 count = Sq.length 时为队满 .
0123
4 5
rearfront
J5J6
J70123
4 5
rearfront
J4 J8
分析可见 , 在 C语言中不能用动态分配的一维数组来实现循环队列 . 如果用户的应用程序中设有循环队列 , 则必须为它设定一个最大队列长度 ; 若用户无法预估所用队列的长度 ,
则宜用链队列 .
Status InitQueue (SqQueue &Q) {
// 构造一个空队列 Q
Q.base = (QElemType *) malloc
(MAXQSIZE *sizeof (QElemType));
if (!Q.base) exit (OVERFLOW);
// 存储分配失败 Q.front = Q.rear = 0;
return OK;
}
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;}
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;
}
例 . 写出以下程序段的输出结果(队列中的元素 类型 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);}
括号匹配的检验1 )凡出现左括弧,则进栈;2 )凡出现右括弧,首先检查栈是否空 若栈空,则表明该“右括弧”多余, 否则和栈顶元素比较, 若相匹配,则“左括弧出栈” , 否则表明不匹配。3 )表达式检验结束时, 若栈空,则表明表达式中匹配正确, 否则表明“左括弧”有余。
括号匹配的检验Status check( ) { //对于输入的任意一个字符串 , 检验括号是否配对
SqStack s;
SElemType ch[80], *p,e; //ch存放字符串
InitStack(s); // 初始化栈成功
Gets(ch); // 输入字符串
p = ch; //p 指向字符串的首字符
... // 输入字符串中括号的检验
括号匹配的检验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 种匹配情况之外的情况
括号匹配的检验{ printf(“ 左右括号不匹配 \n”);
return ERROR;
}
p++;
}
else {// 栈空
printf( “缺乏左括号 \n”);
return ERROR;
} //end if
default: p++; // 其它字符不处理,指针向后移
} //end switch
} //end while
...
括号匹配的检验if (StackEmpty(s)) // 字符串结束时栈空
printf(“ 括号匹配 \n”);
else printf(“缺少右括号 \n”);
}//end check