栈是只能在某一端插入和删除的特殊线性表。
用桶堆积物品,先推进来的压在底下,随后一件一件往上堆。取走时,只能从上面一件一件取。堆和取都在顶部进行,底部一般是不动的。
栈就是一种类似桶堆积物品的数据结构,进行删除和插入的一端称栈顶,另一端称栈底。插入称为进栈(PUSH),删除则称为出栈(POP),栈也称为后进先出表(LIFO表)。
一个栈可以用定长为N的数组S来表示,用一个栈指针TOP指向栈项。若TOP=0,表示栈空,TOP=N时,表示栈满。进栈时TOP加1,出栈时TOP减1,当TOP<0时为下溢。栈指针在运算中永远指向栈项。
一、进栈(PUSH)算法
1、若TOP>=n时,则给出溢出信息,作出错处理(进栈前首先检查栈是否已满,满则溢出;不满则运行第2步)
2、置TOP=TOP+1(栈指针加1,指向进栈地址)
3、S(TOP)=X,结束(X为新进栈的元素)
二、出栈(POP)算法
1、若TOP<=0,则给出下溢信息,作出错处理(出栈前先检查是否已为空栈,空则下溢;不空则运行第2步)
2、X=S(TOP),(出栈后的元素赋给X)
3、TOP=TOP-1,结束(栈指针减1,指向栈顶)
进栈、出栈的Pascal实现过程程序:
const n=100;
type stack=array[1..n] of integer;
procedure push(var s:stack; var top,x:integer); //进栈
begin
if top=n then writeln('overflow')
else begin top:=top+1;s[top]:=x;end;
end;
procedure pop(var s:stack; var top,y:integer); //出栈
begin
if top=0 then writeln('underflow')
else begin y:=s[top];top:=top-1;end;
end;
对于出栈运算中的“下溢”,程序中仅给出了一个标志信息,而在实际应用中,下溢可用来作为控制程序转移的判断标志,是十分有用的。对于入栈运算中的“上溢”,则是一种致命的错误,将使程序无法继续运行,所以要高潮避免。
例1:进制转换
问题描述:读入一个十进制数和n,这个十进制数转换成n进制数
参考程序:
const size=100;
var
a:array[1..size] of integer;
n,d,i,j:integer;
begin
write('Please enter a number(n) base 10:');readln(n);
write('Please enter a number(d):');readln(d);
i:=0;
repeat
inc(i);
a[i]:=n mod d;
n:=n div d;
until n=0;
for j:=i downto 1 do write(a[j]:3);
end.
例2:括号的匹配(表达式的合法性检查)
问题描述:
假设一个表达式有英文字母(小写)、运算符(+、-、*、/)和左右小(圆)括号构成,以“@”作为表达式的结束符。请编写一个程序检查表达式中的左右圆括号是否匹配,若匹配,则返回“YES”;否则返回“NO”。假设表达式长度小于255,左圆括号少于20个。
问题分析:
假设输入的字符串存储在c中(var c:string;)
我们可以定义一个栈:
var
s:array[1..maxn] of char;
top:integer;
用它来存放表达式中从左往右的左圆括号(maxn=20)
算法的思路为:顺序(从左往右)扫描表达式的每个字符c[i],若是“(”,则让它进栈;若遇到的是“)”,则让栈顶元素出栈;当栈发生下溢或当表达式处理完毕而栈非空时,都表示不匹配,返回“NO”;否则表示匹配,返回“YES”。
参考程序:
const maxn=20;
var c:string;
function judge(c:string):boolean;
var
s:array[1..maxn] of char;
top,i:integer;
ch:char;
begin
judge:=true;top:=0;
i:=1;ch:=c[i];
while ch<>'@' do begin
if (ch='(') or (ch=')') then
case ch of
'(': begin top:=top+1;s[top]:='(';end;
')': if top>0 then top:=top-1
else begin judge:=false;exit;end;
end;
i:=i+1;ch:=c[i];
end;
if top<>0 then judge:=false;
end;
begin
readln(c);
if judge(c) then writeln('YES')
else writeln('NO');
end.
例3:编程求一个后缀表达式的值
问题描述:
从键盘读入一个后缀表达式(字符串),只含有0-9组成的运算数及加、减、乘、除(+、-、*、/)四种运算符,每个运算数之间用一个空格隔开,不需要判断给你的表达式是否合法。
算法分析:
后缀表达式的处理过程如下:扫描后缀表达式,凡遇到操作数则将之压进堆栈,遇运算符则从堆栈中弹出两个操作数进行该运算,将运算结束压栈,然后继续扫描,直到后缀表达式被扫描完毕为止,此时栈底元素即为该后缀表达式的值。
如:16-9*(4+3)转换成后缀表达式为:16 9 4 3 +*-
计算结果为-47
参考程序一:
const maxn=20;
var
stack:array[1..maxn] of integer;
s:string;
function comp(s:string):integer;
var
ch:char;
i,top,x,y,z:integer;
begin
top:=0;i:=1;ch:=s[i];
while i<=length(s) do begin
case ch of
'0'..'9': begin
x:=0;
while ch<>' ' do begin
x:=x*10+ord(ch)-48;
i:=i+1;ch:=s[i];
end;
top:=top+1;
stack[top]:=x;
end;
'+': begin x:=stack[top];top:=top-1;y:=stack[top];z:=y+x;stack[top]:=z;end;
'-': begin x:=stack[top];top:=top-1;y:=stack[top];z:=y-x;stack[top]:=z;end;
'*': begin x:=stack[top];top:=top-1;y:=stack[top];z:=y*x;stack[top]:=z;end;
'/': begin x:=stack[top];top:=top-1;y:=stack[top];z:=y div x;stack[top]:=z;end;
end;
i:=i+1;ch:=s[i];
end;
comp:=stack[top];
end;
begin
readln(s);
writeln('result=',comp(s));
end.
参考程序二:
type
stack=record
data:array[1..100] of real;
top:0..100;
end;
var
s:stack;
ch:char;
i:integer;
x:real;
a:array[1..30] of char;
function pop(var s:stack):real; //出栈
begin
pop:=s.data[s.top];
s.top:=s.top-1;
end;
procedure push(var s:stack;x:real); //入栈
begin
s.top:=s.top+1;
s.data[s.top]:=x;
end;
begin
i:=0;
repeat //将表达式存入数组a,以“@”作为表达式的结束符
i:=i+1;read(a[i]);
until a[i]='@';
s.top:=0; //清空栈
i:=1; //i为数组a的下标
ch:=a[i];
while ch<>'@' do begin
case ch of
'0'..'9': begin
x:=0;
while ch<>' ' do begin x:=x*10+ord(ch)-48;i:=i+1;ch:=a[i];end;
end;
'+': x:=pop(s)+pop(s);
'-': begin x:=pop(s);x:=pop(s)-x;end;
'*': x:=pop(s)*pop(s);
'/': begin x:=pop(s);x:=pop(s)/x;end;
end;
push(s,x); //将上面得到的x入栈
i:=i+1;ch:=a[i];
end;
writeln(pop(s));
end.
栈的用途极为广泛,在源程序编译中表达式的计算、过程的嵌套调用和递归调用等都要用到栈。源程序编译中,若要把一个有表达式的赋值语句翻译成正确求值的机器语言,首先应正确地解释表达式,例如:4+8*2-3
一般表达式中会遇到操作数、运算符和语句结束符等,以算术运算符为例,对每种运算赋予一个优先数,如:
运算符:* / + -
优先数:2 2 1 1
(语句结束符“;”的优先数为零)
在运算过程中,优先数高的运算符应先进行运算(但遇到括号时,应另作处理)。按这样的规定,对表达式4+8*2-3自左向右进行运算时,其计算顺序就被唯一地确定下来了。计算机顺序确定后,在对表达式进行编译时,一般设立两个栈,一个称为运算符栈(OPS),另一个称为操作数栈(OVS),以便分别存放表达式中的运算符和操作数。编译程序自左向右扫描表达式直至语句结束,其处理原则是:
1、凡遇到操作数,一律进入操作数栈
2、当遇到运算符时,则将运算符的优先数与运算符栈中的栈顶元素的优先数相比较;若该运算符的优先数大,则进栈;反之,则取出栈项的运算符,并在操作数栈中连续取出两个栈顶元素作为运算对象,并将运算结果存入操作数栈,然后继续比较该运算符与栈顶元素的优先数
对表达式4+8*2-3,当扫描到+号和*号时都要将运算符入栈,接着扫描到-号,其优先数小于*号所以*号退栈,并执行8*2,将结果16再存入操作数栈;再将-号的优先数与运算符栈的栈顶元素+号的优先数相比较,两者相等,所以再将加号退栈,执行4+16,将结果20存入栈,接着由于运算栈已空,所以-号入栈。当扫描到3时,操作数入栈;当扫描到“;”时,其优先数最低,所以-号退栈,并执行20-3,将结果17存入栈,因已扫描到语句结束符,所以表达式的求值结束,结果为17。
例4:模拟计算机处理算术表达式过程,从键盘上输入算术表达式串(只含+、-、*、/运算符,允许含括号),输出算术表达式的值。设输入的且达式串都是合法的。
算法分析:
建立两个栈,一个是操作数栈(number),一个是运算符栈(symbol),根据运算符的优先级对两个栈进行相应的操作。
const
max=100;
var
number:array[0..max] of integer;
symbol:array[1..max] of char;
s,t:string;
i,j,p,code:integer;
procedure push;
begin
inc(p);
symbol[p]:=s[i];
end;
procedure pop;
begin
dec(p);
case symbol[p+1] of
'+': inc(number[p],number[p+1]);
'-': dec(number[p],number[p+1]);
'*': number[p]:=number[p]*number[p+1];
'/': number[p]:=number[p] div number[p+1];
end;
end;
function can:boolean;
begin
can:=true;
if (s[i] in ['+','-']) and (symbol[p]<>'(') then exit;
if (s[i] in ['*','/']) and (symbol[p] in ['*','/']) then exit;
can:=false;
end;
begin
readln(s);
s:='('+s+')';
i:=1;p:=0;
while i<=length(s) do begin
while s[i]='(' do begin
push;inc(i);
end;
j:=i;
repeat
inc(i);
until (s[i]<'0') or (s[i]>'9');
t:=copy(s,j,i-j);
val(t,number[p],code);
repeat
if s[i]=')' then begin
while symbol[p]<>'(' do pop;
dec(P);
number[p]:=number[p+1];
end else begin
while can do pop;
push;
end;
inc(i);
until (i>length(s)) or (s[i-1]<>')');
end;
write('result=',number[0]);
end.
浙公网安备 33010602011771号