栈的应用

栈的应用
栈的应用

[内容提要]

1、栈的概念和特性;

2、栈的存储结构;

3、栈的几种运算(操作)实现;

4、栈的应用举例;

[重点难点]

1、栈的概念和特性;

2、栈的应用场合;

3、栈的操作实现;

[内容讲授]

1.栈的概念和特性

栈(stack)是一种特殊的线性表。作为一个简单的例子,可以把食堂里冼净的一摞碗看作一个栈。在通常情况下,最先冼净的碗总是放在最底下,后冼净的碗总是摞在最顶上。而在使用时,却是从顶上拿取,也就是说,后冼的先取用,后摞上的先取用。好果我们把冼净的碗“摞上”称为进栈(压栈),把“取用碗”称为出栈(弹出),那么,上例的特点是:后进栈的先出栈。然而,摞起来的碗实际上是一个表,只不过“进栈”和“出栈”都在最顶上进行,或者说,元素的插入和删除是在表的一端进行而已。

一般而言,栈是一个线性表,其所有的插入和删除均是限定在表的一端进行,允许插入和删除的一端称栈顶(Top),不允许插入和删除的一端称栈底(Bottom)。若给定一个栈S=(a1, a2,a3,…,a n),则称a1为栈底元素,a n为栈顶元素,元素a i位于元素a i-1之上。栈中元素按a1, a2,a3,…,a n的次序进栈,如果从这个栈中取出所有的元素,则出栈次序为a n, a n-1,…,a1。也就是说,栈中元素的进出是按后进先出的原则进行,这是栈结构的重要特征。因此栈又称为后进先出(LIFO—Last In First Out)表。

我们常用一个图来形象地表示栈,其形式如下图:

通常,对栈进行的运算主要有以下几种:

⑴在使用栈之前,首先需要建立一个空栈,称建栈;

⑵往栈顶加入一个新元素,称进栈(压栈);

⑶删除栈顶元素,称出栈(退栈、弹出);

⑷查看当前的栈顶元素,称读栈;{注意与⑶的区别}

⑸在使用栈的过程中,还要不断测试栈是否为空或已满,称为测试栈。

2.栈的存储结构

栈是一种线性表,在计算机中用向量作为栈的存储结构最为简单。因此,当用编程语言写程序时,用一维数组来建栈十分方便。例如,设一维数组STACK[1..n] 表示一个栈,其中n为栈的容量,即可存放元素的最大个数。栈的第一个元素,或称栈底元素,是存放在STACK[1]处,第二个元素存放在STACK[2]处,第i个元素存放在STACK[i]处。另外,由于栈顶元素经常变动,需要设置一个指针变量top,用来指示栈顶当前位置,栈中没有元素即栈空时,令top=0,当top=n时,表示栈满。

如果一个栈已经为空,但用户还继续做出栈(读栈)操作,则会出现栈的“下溢”;如果一个栈已经满了,用户还继续做进栈操作,则会出现栈的“上溢”。两种情况统称为栈的溢出。

3.对栈的几种运算的实现方法:

(1)建栈

这比较简单,只要建立一个一维数组,再把栈顶指针置为零。栈的容量根据具体的应用要求而定。比如:

type arraytype= array[1.. n] of integer;

var stack:arraytype;

top:integer;

再在程序开始时,置top:=0;

(2)测试栈

测试栈顶指针的值,若top=0,则栈空;若top=n,则栈满。

(3)读栈

若top=0,则栈空,无栈顶元素可读,出错;若top<>0,则回送栈顶元素的值STACK[top]。

(4)进栈(push)

将栈顶指针加1后,再把新元素送到栈顶。假设新元素x为整型,栈的最大深度为n,x和n设置为值型参。而栈和栈顶指针要设置成变量型参。

procedure push(var stack:arraytype;var top:integer;n:integer;x:integer);

begin

if top=n

then begin wr iteln(‘Stack full!’); halt end

else begin top:=top+1; stack[top]:= x end

end;

(5)退栈(pop)

取得栈顶元素的值后,再把栈顶指针top减1。注意都用变量型参。

procedure pop(var stack:arraytype;var top:integer;var x:integer);

begin

if top=0

then begin writeln(‘Stack empty!’); halt end

else begin x:=stack[top]; top:=top-1 end

end;

注意:由于栈本身和栈顶指针是密不可分的,所以有时我们把他们定义成一个记录来处理。如:

type stack=record

vec:array[1..n] of integer; {n为栈可达到的最大深度}

top:integer;

end;

则以上几种运算的实现只要稍做修改即可。

procedure push(var s:stack;x:integer);

begin

if s.top=n

then begin wr iteln(‘Stack full!’); halt end

else begin s.top:=s.top+1; s.vec[s.top]:= x end

end;

procedure pop(var s:stack;var x:integer);

begin

if s.top=0

then begin writeln(‘Stack empty!’); halt end

else begin x:=s.vec[s.top]; s.top:=s.top-1 end

end;

出栈有时也可用函数实现,如:

function pop(var s:stack):integer;

begin

if s.top=0

then begin writeln(‘Stack empty!’); halt end

else begin pop:=s.vec[s.top]; s.top:=s.top-1 end

end;

[栈的应用举例]

1、若已知一个栈的入栈顺序是1,2,3,…,n,其输出序列为P1,P2,P3,…,Pn,若P1是n,则Pi是( C )。

A)i B)n-1 C)n-i+1 D)不确定

2、以下哪一个不是栈的基本运算( B )。

A)删除栈顶元素B)删除栈底的元素C)判断栈是否为空 D)将栈置为空栈

3、设栈S和队列Q初始状态为空,元素e 1 ,e 2 ,e 3 ,e 4 ,e 5 ,e 6依次通过栈S,一

个元素出栈后即进入队列Q,若出队的顺序为e 2 ,e 4 ,e 3 ,e 6 ,e 5 ,e 1 ,则栈S 的容量至少应该为( B )。

A)2 B)3 C)4 D)5

4、设栈S的初始状态为空,现有5个元素组成的序列{1,2,3,4,5},对该序列在S 栈上依次进行如下操作(从序列中的1开始,出栈后不在进栈):进栈,进栈,进栈,出栈,进栈,出栈,进栈,问出栈的元素序列是:_________,栈顶指针的值为______,栈顶元素为:___________________。

解答:出栈序列为{3,4},栈顶指针值为3,栈顶元素为5。

5、设栈S和队列Q初始状态为空,元素e 1 ,e 2 ,e 3 ,e 4 ,e 5 ,e 6依次通过栈S,一个元素出栈后即进入队列Q,若出队顺序为e 2 ,e 4 ,e 3 ,e 6 ,e 5 ,e 1 ,则栈S的容量至少应该为______________。

解答:为3。

6、如下图,有一个无穷大的的栈S,在栈的右边排列着1,2,3,4,5共五个车厢。其中每个车厢可以向左行走,也可以进入栈S让后面的车厢通过。现已知第一个到达出口的是3号车厢,请写出所有可能的到达出口的车厢排列总数(不必给出每种排列)。

出口←←

S↓

解答:9

栈的应用举例(栈与表达式)

[引言]

处理表达式是高级语言的编绎中的一个基本问题。它的实现是栈的一个重要应用,通过对处理表达式的讨论,可以帮助我们进一步了解栈的性能。

[内容讲授]

栈在计算机科学领域有着广泛的应用。比如在编译和运行计算机程序的过程中,就需要用栈进行语法检查(如检查begin和end、“(”和“)”等是否匹配)、计算表达式的值、实现过程和函数的递归调用等。下面举例说明栈在这些方面的应用。

例1、假设一个表达式有英文字母(小写)、运算符(+,—,*,/)和左右小(圆)括号构成,以“@”作为表达式的结束符。请编写一个程序检查表达式中的左右圆括号是否匹配,若匹配,则返回“YES”;否则返回“NO”。表达式长度小于255,左圆括号少于20个。

分析:假设输入的字符串存储在c中(var c:string[255])。

我们可以定义一个栈:var s:array[1..maxn] of char;top:integer;

用它来存放表达式中从左往右的左圆括号。

算法的思路为:顺序(从左往右)扫描表达式的每个字符c[i],若是“(”,则让它进

栈;若遇到的是“)”,则让栈顶元素出栈;当栈发生下溢或当表达式处理完毕而栈非空时,表示不匹配,返回“YES”,否则表示匹配返回“NO”。

程序代码如下:

var c:string;

function doit(c:string):boolean;

var s:array[1..20] of char;

top,i:integer;

ch:char;

begin

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 doit:=false;exit end;

end;

i:=i+1;ch:=c[i];

end;

if top=0 then doit:=true

else doit:=false;

end;

begin

assign(input,'in.txt');

reset(input);

readln(c);

writeln(doit(c));

close(input);

end.

[补充]关于表达式的三种表示法。

1、中缀表达式:a+b

2、后缀表达式:ab+

3、前缀表达式:+ab

4、中缀转后缀的方法及举例转换:

一般方法:把每个运算符移到它的两个运算数后面,每个运算数后多加上一个空格

(为了分隔各个运算数),然后去掉所有括号即可。如:

3/5+6——————————3□5□/□6□+ {□表示空格,下同} 16-9*(4+3)——————19□9□4□3□+*-

2*(x+y)/(1-x)———————2□x□y□+*1□x□-/

(25+x)*(a*(a+b)+b)————25□x□+a□a□b□+*b□+*

另外一种手工方法:可以用后面讲到的二叉表达式树结合先序、中序和后序遍历。

5、中缀表达式的运算规则比较多,包括优先级、左右次序和括号;尤其是括号可以改

变优先顺序,这就只能在计算的每一步,用肉眼去扫描、观察和比较整个表达式中

谁的优先级高,就先计算那部分。但用计算机去做就很麻烦,而且效率不高。所以,计算机科学中是把中缀表达式先转换成后缀表达式,在利用计算机来计算后缀表达

式的值。后缀表达式又称“逆波兰式”,在后缀表达式中,不存在括号,也不存在

优先级的差别,计算过程完全按照运算符出现的先后顺序进行,整个计算过程仅需

一遍扫描即可完成。

6、两个任务:

(1)把中缀表达式转换成后缀表达式;

(2)求后缀表达式的值;

例2、输入一个中缀表达式,编程输出其后缀表达式,要求输出的后缀表达式的运算次序与输入的中缀表达式的运算次序相一致。为简单起见,假设输入的中缀表达式由+(加)、-(减)、×(乘)、/(除)四个运算符号以及左右圆括号和大写英文字母组成,其中算术运算符遵守先乘除后加减的运算规则。假设输入的中缀表达式长度不超过80个字符,且都是正确的,即没有语法错误,并且凡出现括号其内部一定有表达式,即内部至少有一个运算符号。以下是一个运行实例。

输入:X+A*(Y-B)-Z/F

输出:XAYB-*+ZF/-

[算法设计]

设置两个栈:操作数栈(ovs)和运算符栈(ops),用来分别存放表达式中的操作数和运算符。开始时操作数栈为空,运算符栈

中放入一个特殊的标志运算符号#号,并在

表达式的末尾加上一个#号,并规定#号的

优先级最低,然后从左向右扫描表达式,

凡遇操作数便一律进栈;若遇运算符,则

判断其优先级是否大于运算符栈栈顶元素

的优先级。若小,则栈顶运算符退栈,并

从操作数栈中弹出两个操作数(操作数为

后缀表达式)进行后缀变换处理,处理结

果进操作数栈,重复刚才的比较,直到栈

顶运算符的优先级大于等于当前运算符的

优先级,此时,若当前运算符的优先级大

于栈顶运算符的优先级,则当前运算符进

栈,继续扫描;若当前运算符的优先级等

于栈顶运算符的优先级,则弹出栈顶运算

符,继续扫描。扫描完该表达式后运算符栈为空,操作数栈中只有一个元素,该元素就是所

要求的后缀表达式。

以下程序中的数组f用来存放运算符之间的优先级关系,1(>)表示前面的运算符优先于

后面的运算符,-1(<)表示后面的运算符优先于前面的运算符,0(=)表示前面的运算符的优

先级与后面的运算符相同,2(ERROR)表示这两个运算符如果在扫描中相遇的话,意味着该表

达式是错误的。需要补充的是:左括号的优先级是最高的,而里层的左括号比外层的左括号

更优先,右括号的优先级是除#号以外最低的,但左括号和右括号的优先级则是相等的,这

样定义的目的是为了消去括号。以上规律列表如下:

上述算法还可用于求一个表达式的值和判断一个表达式是否有错等等。右上图是对范例

表达式的扫描示意图:

[程序清单]

program ex2(input,output);

const max=100;

op_set:set of char=['+','-','*','/'];

letter:set of char=['A'..'Z'];

var expression,result:string;

procedure scan(expression:string);

var i,top1,top2:integer;

ovs:array [1..max] of string[max];

ops:array [1..max] of char;

f:array['#'..'/','#'..'/'] of shortint;

begin

f['+','+']:=1; f['+','-']:=1; f['+','*']:=-1; f['+','/']:=-1; f['+','(']:=-1; f['+',')']:=1; f['+','#']:=1;

f['-','+']:=1; f['-','-']:=1; f['-','*']:=-1; f['-','/']:=-1; f['-','(']:=-1; f['-',')']:=1; f['-','#']:=1;

f['*','+']:=1; f['*','-']:=1; f['*','*']:=1; f['*','/']:=1; f['*','(']:=-1; f['*',')']:=1; f['*','#']:=1;

f['/','+']:=1; f['/','-']:=1; f['/','*']:=1; f['/','/']:=1; f['/','(']:=-1; f['/',')']:=1; f['/','#']:=1;

f['(','+']:=-1;f['(','-']:=-1;f['(','*']:=-1; f['(','/']:=-1;

f['(','(']:=-1; f['(',')']:=0; f['(','#']:=2;

f[')','+']:=2; f[')','-']:=2; f[')','*']:=2; f[')','/']:=2; f[')','(']:=2; f[']',')'):=2; f[']','#'):=2;

f['#','+']:=-1;f['#','-']:=-1;f['#','*']:=-1; f['#','/']:=-1;

f['#','('):=-1; f['#',']']:=2; f['#','#']:=0;

expression:=expression+'#';

ops[1]:='#';

top1:=0;

top2:=1;

for i:=1 to length(expression) do

begin

if expression[i] in letter

then begin top1:=top1+1;

ovs[top1]:=expression[i]

end

else begin

while f[ops[top2],expression[i]]=1 do

begin

ovs[top1-1]:=ovs[top1-1]+ovs[top1]+ops[top2];

top1:=top1-1;

top2:=top2-1

end;

if f[ops[top2],expression[i]]=0

then top2:=top2-1

else begin top2:=top2+1;

ops[top2]:=expression[i]

end;

end

end;

result:=ovs[1]

end;

begin{main}

write('Input a expression:');

readln(expression);

scan(expression);

writeln('The result is: ',result)

end.

测试输入:A*(X+Y)/(B-Z)

输出:AXY+*BZ-/

例3、编程求一个后缀表达式的值。从键盘读入一个后缀表达式(字符串),只含有0-9组成的运算数及加(+)、减(—)、乘(*)、除(/)四种运算符。每个运算数之间用一个空格隔开,不需要判断给你的表达式是否合法。

分析:先读入后缀表达式(字符串)。后缀表达式的处理过程如下:扫描后缀表达式,凡遇操作数则将之压进堆栈,遇运算符则从堆栈中弹出两个操作数进行该运算,将运算结果压栈,然后继续扫描,直到后缀表达式被扫描完毕为止,此时栈底元素即为该后缀表达式的值。程序:

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)-ord('0');

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;{while}

comp:=stack[top];

end;

begin{main}

writeln('input a string(@_over):');

readln(s);

writeln('result= ',comp(s));

readln;

end.

[作业与思考]

请把本节的内容组合在一起,编一个程序,输入一个中缀表达式(由0-9组成的运算数、加+减—乘*除/四种运算符、左右小括号组成。注意“—”也可作为负数的标志,表达式以“@”作为结束符),判断表达式是否合法,如果不合法,请输出“NO”;否则请把表达式转换成后缀形式,再求出后缀表达式的值并输出。

注意:必须用栈操作,不能直接输出表达式的值。

如果再考虑是实数运算呢?

栈与递归

[递归算法及在计算机中的实现]

1、递归算法

例1、用递归算法把任一给定的十进制正整数(<=32000)转换成八进制数输出,程序如下:var m:integer;

procedure tran(n:integer); {递归过程}

var k:integer;

begin

k:=n mod 8;

n:=n div 8;

if n<>0 then tran(n);

write(k:1)

end;

begin {主程序}

write('input m:');

readln(m);

write(m,'=(');

tran(m);

writeln(')8');

readln;

end.

输入:m=765 {下划线表示输入}

输出:765=(1375)8

例2、用递归算法求N阶乘(N!=1*2*3*……*N,N<20);

var n:integer;

function f(n:integer):longint; {递归函数,N=20时,超过maxlongint}

begin

if n=0 then f:=1

else f:=n*f(n-1)

end;

begin {主程序}

write('input n:');

readln(n);

write(n,'!=',f(n));

end.

2、递归在计算机中的实现

计算机执行递归算法时,是通过栈来实现的。具体说来,就是在(递归过程或递归函数)开始运行时,系统首先为递归建立一个栈,该栈的元素类型(数据域)包括值参、局部变量和返回地址;在每次执行递归调用语句时之前,自动把本算法中所使用的值参和局部变量的当前值以及调用后的返回地址压栈(一般形象地称为“保存现场”,以便需要时“恢复现场”返回到某一状态),在每次递归调用结束后,又自动把栈顶元素(各个域)的值分别赋给相应的值参和局部变量(出栈),以便使它们恢复到调用前的值,接着无条件转向(返回)由返回地址所指定的位置继续执行算法。

具体到上面的例1中,当遇到递归调用tran(n)时,系统首先为后面的递归调用建立一个含有3个域(值参n,局部变量k和一个返回地址)的栈;在每次执行递归调用tran(n)前,系统自动把n和k的当前值以及write(k:1)语句的开始位置(即调用结束后的返回地址)压栈;在每次执行到最后的end语句(即一次递归调用结束)后,又自动把栈顶的与n 和k对应的值分别赋给n和k(出栈),接着无条件转向write(k:1)语句的开始位置继续向下执行程序。

3、递归的缺点

早期操作系统DOS的内核是限制只能使用640K内存(当时的机器内存也很小),在此之上的BP运行时,可用的所有内存空间最大也只能为640K,其中程序代码、常量、变量和堆栈(局部变量、子程序参数、返回地址;即递归中的栈)各占64K,在BP中的OPTIONS菜单的MEMORY SIZE子菜单中可以修改STACK SIZE至多到64K(一般设置为65520),可以解决很多递归程序遇到的栈溢出错误。

但有时还是不够,这时就想用640K以内(64K以上)的多余空间,方法是手工开辟一个栈空间模拟系统处理递归的实现方法,这样就可以把一个递归过程转换成非递归过程,一方面可以进一步加深对栈和递归的理解,另一方面也可以解决一些递归无法解决的问题。但带来的问题是程序会很复杂。

[递归转换为非递归]

设P是一个递归算法,假定P中共有m个值参和局部变量,共有t处递归调用P的语句,则把P改写成一个非递归算法的一般规则为:

1、定义一个栈S,用来保存每次递归调用前值参和局部变量的当前值以及调用后的返回地

址。即S应该含有m+1个域,且S的深度必须足够大,使得递归过程中不会发生栈溢出。

2、定义t+2个语句标号,其中用一个标号标在原算法中的第一条语句上,用另一个标号标

在作返回处理的第一条语句上,其余t个标号标在t处递归调用的返回地址,分别标在相应的语句上。

3、把每一个递归调用语句改写成如下形式:

(1)把值参和局部变量的当前值以及调用后的返回地址压入栈;

(2)把值参所对应的实在参数表达式的值赋给值参变量;

(3)无条件转向原算法的第一条语句;

4、在算法结束前增加返回处理,当栈非空时做:

(1)出栈;

(2)把原栈顶中前m个域的值分别赋给各对应的值参和局部变量;

(3)无条件转向由本次返回地址所指定的位置;

5、增设一个同S栈的成分类型(元素)相同的变量,作为进出栈的缓冲变量,对于递归函

数,还需要再增设一个保存函数值中间结果的临时变量,用这个变量替换函数体中的所有函数名,待函数结束之前,在把这个变量的值赋给函数名返回。

6、在原算法的第一条语句之前,增加一条把栈置空的语句。

7、对于递归函数而言,若某条赋值语句中包含两处或多处递归调用(假设为n处),则应

首先把它拆成n条赋值语句,使得每条赋值语句只包含一处递归调用,同时对增加的n-1条赋值语句,要增设n-1个局部变量,然后按以上六条规则转换成非递归函数。

[应用举例]

例3、把例1中的递归过程改写成非递归过程。

procedure tran(n:integer); {非递归过程}

label 1,2,3; {因为只有1处递归调用,所以定义t+2=3个标号}

type node=record {定义栈的成分类型,因为值参和局部变量共2个,所以m+1=3个域} n:integer; {值参n 的域}

k:integer; {局部变量k的域}

r:integer; {返回地址的域}

end;

stack=record {定义一个栈类型,包括数据域(一个数组)和一个栈顶指针域} vec:array[1..7] of node; {32000以内的十进制正整数转换成八进制数,不会

超过七位数,数组元素类型为node类型}

top:integer; {栈顶指针}

end;

var s:stack; {定义栈变量}

x:node; {进出栈的缓冲变量}

k:integer; {原来的局部变量}

procedure push(var s:stack;x:node); {进栈过程,注意s 一定要定义成变量型参数} begin {因为栈的变化要带出过程}

if s.top=7 then begin write('up-overflow');exit;end

else begin s.top:=s.top+1;s.vec[s.top]:=x;end;

end;

procedure pop(var s:stack;var x:node); {出栈过程,都要定义成变量型参。一方面出栈的元素存放在x中要带出过程,另外栈顶指针也变化了,所以s也要定义成变量型参} begin

if s.top=0 then begin write('down-overflow');exit;end

else begin x:=s.vec[s.top];s.top:=s.top-1;end;

end;

begin

s.top:=0; {按照第6条}

1:k:=n mod 8; {按照第2条的红色语句}

n:=n div 8;

if n<>0 then begin {按照第3条,3个步骤,本题不需要第(2)小句}

x.n:=n;

x.k:=k;

x.r:=2;

push(s,x); {(1)}

goto 1; {(3)}

end;

2:write(k:1); {按照第2条的蓝色语句}

3:if s.top>0 then begin {按照第4条,3个步骤}

pop(s,x); {(1)}

n:=x.n;

k:=x.k; {(2)}

goto 2; {(3)}

end;

end; {建议:单步跟踪各个变量,观察理解过程}

例4、把例2中的递归函数改写成非递归函数

function f(n:integer):longint; {非递归函数}

label 1,2,3;

var s:array[1..20] of integer;{栈,必须大于等于n,保证不溢出}

top:integer; {栈顶}

f1:longint; {保存中间结果的临时变量}

begin

top:=0; {栈的初始化}

1:if n=0 then begin f1:=1;goto 3;end {遇到边界就结束转返回处理}

else begin top:=top+1; {否则,进栈}

a[top]:=n;

n:=n-1; {实参减1}

goto 1; {转向开始,继续}

end;

2:f1:=n*f1; {根据n和f(n-1),求f(n)}

3:if top>0 then begin n:=a[top]; {做返回处理}

top:=top-1;

goto 2; {转向返回地址}

end;

f:=f1; {赋值}

end;

注意:

1、上面的程序其实已经进行了简化,一是栈只设置了一个保存值参n的域,二是

忽略了缓冲变量,而直接用n,三是省略了返回地址,因为每个递归调用的返

回地址都相同;

2、以上算法中,从标号1到goto 1所构成的循环,实际上是一个递推过程;从n

推到0为止;从标号2到goto 2所构成的循环是一个回代过程;

3、假设n=5,请大家画出栈的变化情况。

[小结思考]

从以上可以看出,递归算法简单直观,是整个计算机算法和程序设计领域一个非常重要的方面,必须熟练掌握和应用它。但计算机的执行过程比较复杂,需要用系统栈进行频繁的进出栈操作和转移操作。递归转化为非递归后,可以解决一些空间上不够的问题,但程序太复杂。所以,并不是一切递归问题都要设计成非递归算法。实际上,很多稍微复杂一点的问题(比如:二叉树的遍历、图的遍历、快速排序等),不仅很难写出它们的非递归过程,而且即使写出来也非常累赘和难懂。在这种情况下,编写出递归算法是最佳选择,有时比较简单的递归算法也可以用迭代加循环或栈加循环的方法去实现。如:

function f(n:integer):integer; {求第n个fibonacci数,迭代+循环}

var I,f1:integer;

begin

i:=0;

f1:=1;

while i

f:=f1;

end;

procedure tran(n:integer);{例1改写成:栈+循环}

var s:array[1..7] of integer;

I,top:integer;

Begin

Top:=0;

While n<>0 do

Begin

Top:=top+1;

s[top]:=n mod 8;

n:=n div 8;

end;

for i:=top downto 1 do write(s[i]:1);

End;

[栈与回溯法]

由于回溯法采用的也是递归算法,所以在实现时也是用栈实现的。当然,回溯法的程序也可以改成非递归的、用栈模拟执行。比如下面的这个程序是验证“四色原理”的,请你改写成非递归算法。

const num=20; {最多20个区域}

var a:array [1..num,1..num] of 0..1;{用邻接矩阵表示图,0—表示两个区域不相邻,

1—表示相邻}

s:array [1..num] of 0..4; {用1-4分别代表RBWY四种颜色;0代表末填进任何颜色} k1,k2,n:integer;

function pd(i,j:integer):boolean;{判断可行性:第I个区域填上第J种颜色}

var k:integer;

begin

for k:=1 to i-1 do

if (a[i,k]=1) and (j=s[k]) {区域I和J相邻且将填进的颜色和已有的颜色相同} then begin pd:=false; exit; end;

pd:=true;

end;

procedure print;{打印结果}

var k:integer;

begin

for k:=1 to n do {将数字转为RBWY串输出}

case s[k] of

1:write('R':4);

2:write('B':4);

3:write('W':4);

4:write('Y':4);

end;

writeln;

end;

procedure try(i:integer); {递归回溯}

var j:integer;

begin

for j:=1 to 4 do

if pd(i,j) then begin

s[i]:=j;

if i=n then print

else try(i+1);

s[i]:=0;

end;

end;

BEGIN {主程序,输入一个图的邻接矩阵,输出一种“四色”填色方案}

write('please input city number: '); readln(n);

writeln('please input the relation of the cities:');

for k1:=1 to n do {读入邻接矩阵}

begin

for k2:=1 to n do read(a[k1,k2]);

readln;

end;

for k1:=1 to n do s[k1]:=0; {初始化}

try(1);

END.

[作业]

作业1、用递归算法计算Fibonacci数列的任一项。再改写成非递归算法(用栈模拟)。

Fibonacci数列为以下形式的一系列整数:0 1 1 2 3 5 8 13 21 34 55 89 144……

作业2、用递归算法实现hanoi(汉诺塔问题)。再改写成非递归算法(用栈模拟)。

队列的基本知识及应用

[内容提要]

1.通过本章学习,掌握队列的定义及队列的存储结构

2.掌握队列的基本操作运算:建队、插入、删除、队列空等,用数组、链接方式所建立队列及操作运算

3.掌握循环队列概念及运算

4.能够利用队列解决一些实际问题:广度优先搜索算法

[重点难点]

1.队列、循环队列概念及存储结构

2.队列的基本操作

3.综合运用队列结构解决实际问题

[内容讲授]

一、队列的基本知识

队列(Queue)是一种特殊的线性表。它是一种运算受限的线性表。它只允许在表的一端进行插入,而在另一端进行删除。允许删除的一端称为队头(front),允许插入的一端称为队尾(rear)。因此队列亦称作先进先出(First In First Out)的线性表,简称FIFO表。

1.队列的性质

假设队列为a1,a2,..,a n,那么a1就是队头元素,a n为队尾元素。队列中的元素是按a1,a2,..,a n 的顺序进入的,退出队列也只能按照这个次序依次退出。也就是说,只有在a1离开队列之后,a2才能退出队列,只有在a1,a2,..,a n-1都离开队列之后,a n才能退出队列。图1是队列的示意图。

图1 队列的先进先出示意图

2.队列的存储结构

(1)顺序存储:可用记录数组实现

(2)链接存储:用链接存储方式实现

如图所示:

链队示意图rear

front

3.基本术语:

(1) 队空:当队列中没有元素时称为空队列。

(2) 队满:当队列中单元全部被占用

(3) 队列操作规则:

在队列中依次加入元素a1,a2,…an之后,a1是队头元素,an是队尾元素。其出队操作规定从队头进行,进队操作从队尾进行。

即队列的操作是依先进先出的原则进行的。

(4) 上溢、下溢:真溢、假溢

4.队列的基本操作

用顺序队列存储结构的表示方法:

type queue=record

vec : array[1..m] of elemtype

f, r : integer ;

end;

f, r 分别指向队列的头和尾

(1)进队操作(插入运算)

Procedure insert( q, x ) ;

begin

①if q.r =m then 输出上溢

②q.r := q.r+1

③q.vec[q.r]:=x ; { 进入队尾}

④if q.f= 0 then q.f:=1

end;

(2)出队操作:删除操作

procedure dele (q , x) ;

begin

①if q.f=0 then 输出下溢

②x=q.vec[q.f]

③if q.f=q.r then [ q.f=0; q.r =0 ]

else q.f:=q.f + 1

end;

(3)置空队算法

procedure setnull (Q) ;

begin

q.f:=0 ; q.r:=0 ;

end;

5. 循环队列

为充分利用向量空间,克服"假上溢"现象的方法是:将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。存储在其中的队列称为循环队列(Circular Queue)。

(1)定义:将队列的首、尾连接起来,形成一个环状。队尾指向m ,队首指向1。

对循环队列的操作:

(2)插入操作:

procedure insert2(q.x);

begin

●if (q.r mod m)+1 =q.f then 溢出

else q.r:= [ (q.r mod m)+1 ;q.vec[q.r]:=x ]

end;

(3) 删除操作:

procedure delete2( q, x ) ;

begin

if q.f=q.r then 输出队空

else [ q.f = ( q.f mod m ) +1 ;x=q.vec[q.f ] ]

end;

(4) 循环队列的长度:

( r- f+ n ) mod n

6. 链队列

链队是指队列的链接存储表示,也就是说它只允许在表尾进行插入和在表头进行删除的单链表。一个链队需要队首、队尾两个指针,一个指向表头,一个指向表尾,如下图所示:

设有如下的数据类型定义:

type linklist= ^dynanode ;

dynanode = record

data : elemtype ;

next : linklist ;

end;

type linkqueue=record

f, r : linklist ;

end;

链接队列的操作运算如下:

(1)插入算法

procedure insert ( HQ,x) ;

begin

●new(p) ; p^.data:=x ; p^.next := nil ;

●if HQ.r = nil then [ HQ.f := p; HQ.r :=p ]

else [ HQ.r^.next := p ; HQ.r := p ]

end;

(2)删除算法

procedure delete (HQ, x ) ;

begin

●if HQ.f= nil then error ( ‘ underflow ‘); { 队为空}

●x:=HQ.f ^.data ;

●p:= HQ.f ;

●if HQ.f = HQ.r then [ HQ.f:= nil ; HQ.r := nil ] { 删除结点}

else HQ.f := HQ.f ^ .next ;

●dispose (p) ;

end;

二、队列的应用

队列在日常生活中应用很多,特别是在计算机科学领域中所起的作用很大。例如在解决主机与外部设备之间速度不匹配问题,解决多用户引起的资源竞争问题等,都运用了队列这样的数据结构算法,下面通过一些实例,了解运用队列解决问题方法。运用队列解决广度优先搜索算法中的最短路径问题是一种比较好的算法。

例题1. 1995年高中组基础题第4 题,从入口(1)到出口(17)的可行路线图中,数字标号表示关卡:

现将上面的路线图,按记录结构存储如下:

栈的类型定义与基本操作

循环队链的出队 bool Dequeue( CSQueue &q, QElemType &e ) { int front; if( q.length == 0 ) return false; front = ( q.rear + 1 - q.length + MAXQSIZE ) % MAXQSIZE; e = q.elem[ front ]; q.length --; return true; } 循环队链的入队 bool Enqueue( CSQueue &q, QElemType e ) { if( q.length == MAXQSIZE ) return false; q.rear = ( q.rear + 1 ) % MAXQSIZE; q.elem[ q.rear ] = e; q.length ++; return true; } 链队的入队 void Enqueue( LQueue &q, QElemType e ) { LQueuePtr p; p = new QNode; p->data = e; p->next = q.rear->next; q.rear->next = p; q.rear = p; } 链队的出队 bool Dequeue( LQueue &q, QElemType &e ) { LQueuePtr p; if( q.rear->next == q.rear ) return false; p = q.rear->next; e = p->next->data; q.rear->next = p->next; delete p; return true; } 顺序栈的类型定义与基本操作:

栈的基本操作与应用

实验报告 课程名称数据结构实验名称栈的基本操作与应用 姓名王灵慧专业班级软工18104 学号 201817040409 试验日期 2019-11-06试验地点E3-502指导老师邹汉斌成绩 一、实验目的 1.熟悉并能实现栈的定义和基本操作。 2.了解和掌握栈在递归和非递归算法的应用。 二、实验要求 1.进行栈的基本操作时要注意栈“后进先出”的特性。 2.编写完整程序完成下面的实验内容并上机运行。 3.整理并上交实验报告。 三、实验内容 1.编写程序任意输入栈长度和栈中的元素值,构造一个顺序栈,对其进行清空、销毁、入栈、出栈以及取栈顶元素操作。 2.已知函数t(n)=2*t(n/2)+n 其中t(0)=0,n为整数。编写程序实现: (1)计算t(n)的递归算法。 (2)分别用链式栈和顺序栈实现计算t(n)的非递归算法。 四、思考与提高 1.如果一个程序中要用到两个栈,为了不发生上溢错误,就必须给每个栈预先分配一个足够大的存储空间。若每个栈都预分配过大的存储空间,势必会造成系统空间紧张。如何解决这个问题? 五、实验步骤(每个实验内容包含代码、输入、输出、错误分析): 1、实验内容(1): #include #include #include #define true 1 #define null 0 #define ok 1 #define error 0 #define overflow -1 #define stack_init_size 100 #define stackincrement 10 using namespace std; typedef int selemtype; typedef int status; typedef struct { selemtype *base; selemtype *top; int stacksize; } sqstack; status initstack(sqstack &s) { s.base=(selemtype *)malloc(stack_init_size * sizeof(selemtype)); if(!s.base)exit(overflow);

栈和队列的基本操作的实现

封面: 安徽大学 网络工程 栈和队列的基本操作的实现 ______2010\4\12

【实验目的】 1.理解并掌握栈和队列的逻辑结构和存储结构; 2.理解栈和队列的相关基本运算; 3.编程对相关算法进行验证。 【实验内容】 (一)分别在顺序和链式存储结构上实现栈的以下操作(含初始化,入栈,出栈,取栈顶元素等): 1.构造一个栈S,将构造好的栈输出; 2.在第1步所构造的栈S中将元素e 入栈,并将更新后的栈S输出; 3.在第2步更新后所得到的栈S中将栈顶元素出栈,用变量e返回该元素,并将更新后的栈S输出。(二)分别在链队列和循环队列上实现以下操作(初始化,入队,出队,取队头元素等): 1.构造一个队列Q,将构造好的队列输出; 2.在第1步所构造的队列Q中将元素e入队,并将更新后的队列Q输出; 3.在第2步更新后所得到的队列Q中将队头元素出队,用变量e返回该元素,并将更新后的队列Q输出。

【要求】 1.栈和队列中的元素要从终端输入; 2.具体的输入和输出格式不限; 3.算法要具有较好的健壮性,对运行过程中的错误 操作要做适当处理。 三、实验步骤 1.本实验用到的数据结构 (1)逻辑结构:线性结构 (2)存储结构:程序一、四(顺序存储结构); 程序二、三(链式存储结构); 2.各程序的功能和算法设计思想 程序一:顺序栈 # include # include # include #define STACKINITISIZE 100 # define STACKINCREMENT 10 # define OK 1 # define ERROR 0 # define OVERFLOW -2 typedef int SElemtype; typedef int status; typedef struct { SElemtype *base; SElemtype *top; int stacksize; }sqstack; void Initstack (sqstack *s) { (*s).base = (SElemtype *)malloc(STACKINITISIZE * sizeof (SElemtype)); if(!(*s).base) exit(OVERFLOW);

顺序栈的基本操作讲解

遼穿紳範大學上机实验报告 学院:计算机与信息技术学院 专 业 : 计算机科学与技术(师 范) 课程名称:数据结构 实验题目:顺序栈的基本操作 班级序号:师范1班 学号:201421012731 学生姓名:邓雪 指导教师:杨红颖 完成时间:2015年12月25号 一、实验目的: 1 ?熟悉掌握栈的定义、结构及性质; 2. 能够实现创建一个顺序栈,熟练实现入栈、出栈等栈的基本操作; 3?了解和掌握栈的应用。 二、实验环境: Microsoft Visual C++ 6.0

三、实验内容及要求: 栈是一种特殊的线性表,逻辑结构和线性表相同,只是其运算规则有更多的限制,故又称为受限的线性表。 建立顺序栈,实现如下功能: 1. 建立一个顺序栈 2. 输出栈 3. 进栈 4. 退栈 5. 取栈顶元素 6. 清空栈 7. 判断栈是否为空 进行栈的基本操作时要注意栈”后进先出”的特性。 四、概要设计: 1、通过循环,由键盘输入一串数据。创建并初始化一个顺序栈。 2、编写实现相关功能函数,完成子函数模块如下。 3、调用子函数,实现菜单调用功能,完成顺序表的相关操作

五、代码: #include #include #define maxsize 64 typedef int datatype; //定义结构体typedef struct { datatype data[maxsize]; int top; }seqstack; //建立顺序栈seqstack *SET(seqstack *s) { int i; s=(seqstack*)malloc(sizeof(seqstack)); s->top=-1; printf(" 请输入顺序栈元素(整型,以scanf("%d",&i); do{ s->top++; s->data[s->top]=i; scanf("%d",&i); 0 结束):"); }while(i!=0); printf(" 顺序栈建立成功\n"); return s; } //清空栈void SETNULL(seqstack *s) { s->top=-1;} //判断栈空 int EMPTY(seqstack *s) { if(s->top>=0) return 0; else return 1;} //进栈 seqstack *PUSH(seqstack *s) { int x; printf(" 你想要插入的数字:"); scanf("%d",&x); if(s->top==maxsize-1) { printf("overflow"); return NULL; } else {

栈的表示及栈的应用

实验二:栈的表示及栈的应用 【实验目的】 (1) 掌握栈的顺序存储结构及其基本操作的实现。 (2) 掌握栈后进先出的特点,并利用其特性在解决实际问题中的应用。 (3) 掌握用递归算法来解决一些问题。 【实验内容】 1. 编写程序,对于输入的任意一个非负十进制整数,输出与其等值的八进制数(课本P48)。 2. 编写递归程序,实现以下函数的求解(课本P54)。 3. 编写程序,实现Hanoi 塔问题(课本P55-P58)。 【实验步骤】 1.打开VC++。 2.建立工程:点File->New ,选Project 标签,在列表中选Win32 Console Application ,再在右边的框里为工程起好名字,选好路径,点OK->finish 。至此工程建立完毕。 3.创建源文件或头文件:点File->New ,选File 标签,在列表里选C++ Source File 。给文件起好名字,选好路径,点OK 。至此一个源文件就被添加到了你刚创建的工程之中。 4.写好代码 5.编译->链接->调试 【主要代码】进制转换 #include #include #define OK 1 #define ERROR 0 ???>-+-==1 ),2()1(0,1,)(n n Fib n Fib n n n Fib

#define OVERFLOW -2 typedef int Status; typedef int SElemType; #define STACK_INIT_SIZE 100 #define STACKINCREMENT 10 typedef struct { SElemType *base; SElemType *top; int stacksize; }SqStack; Status InitStack(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; return OK; }//InitStack Status Push(SqStack &S, SElemType e){ //插入元素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; } //PUSH Status Pop(SqStack &S, SElemType &e) { //若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR

数据结构栈的定义及基本操作介绍

北京理工大学珠海学院实验报告 ZHUHAI CAMPAUS OF BEIJING INSTITUTE OF TECHNOLOGY 班级软件工程3班学号 150202102309姓名郭荣栋 指导教师余俊杰成绩 实验题目栈的实现与应用实验时间 一、实验目的、意义 (1)理解栈的特点,掌握栈的定义和基本操作。 (2)掌握进栈、出栈、清空栈运算的实现方法。 (3)熟练掌握顺序栈的操作及应用。 二、实验内容及要求 1.定义顺序栈,完成栈的基本操作:建空栈、入栈、出栈、取栈顶元素(参见教材45页)。 2. 调用栈的基本操作,将输入的十进制数转换成十六进制数。 3. 调用栈的基本操作,实现表达式求值,如输入3*(7-2)#,得到结果15。 三、实验结果及分析 (所输入的数据及相应的运行结果,运行结果要有提示信息,运行结果采用截图方式给出。)

四、程序清单(包含注释) 1、2. #include #include #include using namespace std; #define OK 1 #define ERROR 0 #define OVERFLOW -2 #define MAXSIZE 100 #define INCREASEMENT 10 #define STACK_INIT_SIZE 100 #define STACKINCREMENT 10

typedef int SElemType; typedef int Status; typedef struct{ SElemType *base; SElemType *top; int stacksize; }Sqstack; void StackTraverse(Sqstack S) { while (S.top != S.base) { cout << *(S.top-1) << endl; S.top--; } } Status InitStack(Sqstack &S){ S.base=(SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType)); if(!S.base){ exit(OVERFLOW); }

数据结构栈的基本操作,进栈,出栈

第五次实验报告—— 顺序栈、链栈的插入和删除一需求分析 1、在演示程序中,出现的元素以数字出现定义为int型, 2、演示程序在计算机终端上,用户在键盘上输入演示程序中规定的运算命令,相应的输入数据和运算结果显示在终端上 3、顺序栈的程序执行的命令包括如下: (1)定义结构体 (2)顺序栈的初始化及创建 (3)元素的插入 (4)元素的删除 (5)顺序栈的打印结果 3、链栈的程序执行的命令包括如下: (1)定义结构体 (2)链栈的初始化及创建 (3)元素的插入 (4)元素的删除 (5)链栈的打印结果 二概要设计 1、顺序栈可能需要用到有序表的抽象数据类型定义: ADT List{ 数据对象:D={ai|ai∈ElemL, i=1,2,...,n, n≥0} 数据关系:R1={|ai-1,ai ∈D, i=2,...,n } 基本操作: InitStack(SqStack &S) 操作结果:构造一个空栈 Push(L,e) 操作结果:插入元素e为新的栈顶元素

Status Pop(SqStack &S) 操作结果:删除栈顶元素 }ADT List; 2、链栈可能需要用到有序表的抽象数据类型定义: ADT List{ 数据对象:D={ai|ai∈ElemL, i=1,2,...,n, n≥0} 数据关系:R1={|ai-1,ai ∈D, i=2,...,n } 基本操作: LinkStack(SqStack &S) 操作结果:构造一个空栈 Status Push(L,e) 操作结果:插入元素e为新的栈顶元素 Status Pop(SqStack &S) 操作结果:删除栈顶元素 }ADT List; 3、顺序栈程序包含的主要模块: (1) 已给定的函数库: (2)顺序栈结构体: (3)顺序栈初始化及创建: (4)元素插入 (5)元素删除

栈和队列的基本操作实现及其应用

实验二栈和队列的基本操作实现及其应用 一_一、实验目的 1、熟练掌握栈和队列的基本操作在两种存储结构上的实现。 一_二、实验内容 题目一、试写一个算法,判断依次读入的一个以@为结束符的字符序列,是否为回文。所谓“回文“是指正向读和反向读都一样的一字符串,如“321123”或“ableelba”。 相关常量及结构定义: #define STACK_INIT_SIZE 100 #define STACKINCREMENT 10 typedef int SElemType; typedef struct SqStack { SElemType *base; SElemType *top; int stacksize; }SqStack; 设计相关函数声明: 判断函数:int IsReverse() 栈:int InitStack(SqStack &S )

int Push(SqStack &S, SElemType e ) int Pop(SqStack &S,SElemType &e) int StackEmpty(s) 一_三、数据结构与核心算法的设计描述 1、初始化栈 /* 函数功能:对栈进行初始化。参数:栈(SqStack S)。 成功初始化返回0,否则返回-1 */ int InitStack(SqStack &S) { S.base=(SElemType *)malloc(10*sizeof(SElemType)); if(!S.base) //判断有无申请到空间 return -1; //没有申请到内存,参数失败返回-1 S.top=S.base; S.stacksize=STACK_INIT_SIZE; S.base=new SElemType; return 0; } 2、判断栈是否是空 /*函数功能:判断栈是否为空。参数; 栈(SqStack S)。栈为空时返回-1,不为空返回0*/ int StackEmpty(SqStack S) { if(S.top==S.base) return -1; else return 0; } 3、入栈 /*函数功能:向栈中插入元素。参数; 栈(SqStack S),元素(SElemtype e)。成功插入返回0,否则返回-1 */ int Push(SqStack &S,SElemType e) { if(S.top-S.base>=S.stacksize) { S.base=(SElemType *)realloc(S.base,(S.stacksize+1) * sizeof(SElemType));

用顺序结构表示栈并实现栈地各种基本操作

栈的顺序表示和实现 2.2基础实验 2.2.1实验目的 (1)掌握栈的顺序表示和实现 (2)掌握栈的链式表示和实现 (3)掌握队列的顺序表示和实现 (4)掌握队列的链式表示和实现 2.2.2实验内容 实验一:栈的顺序表示和实现 【实验内容与要求】 编写一个程序实现顺序栈的各种基本运算,并在此基础上设计一个主程序,完成如下功能: (1)初始化顺序栈 (2 )插入元素 (3)删除栈顶元素 (4)取栈顶元素 (5)遍历顺序栈 (6)置空顺序栈 【知识要点】 栈的顺序存储结构简称为顺序栈,它是运算受限的顺序表。 对于顺序栈,入栈时,首先判断栈是否为满,栈满的条件为:p->top= =MAXNUM-1 ,栈满时,不能入栈;否则岀现空间溢岀,引起错误,这种现象称为上溢。 岀栈和读栈顶元素操作,先判栈是否为空,为空时不能操作,否则产生错误。通常栈空作为一种控制转移的条件。 注意: (1)顺序栈中元素用向量存放 (2)栈底位置是固定不变的,可设置在向量两端的任意一个端点 (3)栈顶位置是随着进栈和退栈操作而变化的,用一个整型量top (通常称top为栈顶指针)来指示当前栈顶位置 【实现提示】 /*定义顺序栈的存储结构*/

typedef struct { ElemType stack[MAXNUM]; int top; }SqStack; /*初始化顺序栈函数*/ void lnitStack(SqStack *p) {q=(SqStack*)malloc(sizeof(SqStack)/* 申请空间*/) /*入栈函数*/ void Push(SqStack *p,ElemType x) {if(p->toptop=p->top+1; /* 栈顶+1*/ p->stack[p->top]=x; } /* 数据入栈*/ } /*岀栈函数*/ ElemType Pop(SqStack *p) {x=p->stack[p->top]; /* 将栈顶元素赋给x*/ p->top=p->top-1; } /* 栈顶-1*/ /*获取栈顶元素函数*/ ElemType GetTop(SqStack *p) { x=p_>stack[p_>top];} /*遍历顺序栈函数*/ void OutStack(SqStack *p) { for(i=p->top;i>=0;i--) printf("第%d 个数据元素是:%6d\n",i,p->stack[i]);} /*置空顺序栈函数*/ void setEmpty(SqStack *p) { p->top= -1;} 【参考程序】 #include #include #define MAXNUM 20 #define ElemType int /*定义顺序栈的存储结构*/ typedef struct { ElemType stack[MAXNUM]; int top; }SqStack; /*初始化顺序栈*/ void InitStack(SqStack *p) { if(!p) printf("Eorror");

栈的基本操作c语言

#include #include #include //函数结果状态代码 #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define INFEASIBLE -1 #define OVERFLOW -2 //Status 是函数的类型,其值是函数结果状态代码 typedef int Status; typedef int SetElemType; typedef SetElemType ElemType; #include "tou.h" #include #include typedef char SElemType; // 栈的元素类型 #define STACK_INIT_SIZE 100 // 存储空间初始分配量 #define STACKINCREMENT 10 // 存储空间分配增量 // 栈的顺序存储表示P46 typedef struct SqStack { SElemType *base; // 在栈构造之前和销毁之后,base的值为NULL SElemType *top; // 栈顶指针 int stacksize; // 当前已分配的存储空间,以元素为单位 }SqStack; // 顺序栈 // 构造一个空栈S。 int InitStack(SqStack *S) { // 为栈底分配一个指定大小的存储空间 (*S).base = (SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType)); if( !(*S).base ) exit(OVERFLOW); // 存储分配失败 (*S).top = (*S).base; // 栈底与栈顶相同表示一个空栈

数据结构_实验三_栈和队列及其应用

实验编号:3四川师大《数据结构》实验报告2016年10月29日 实验三栈与队列及其应用_ 一.实验目得及要求 (1)掌握栈与队列这两种特殊得线性表,熟悉它们得特性,在实际问题背景下灵活运用它们; (2)本实验训练得要点就是“栈”得观点及其典型用法; (3)掌握问题求解得状态表示及其递归算法,以及由递归程序到非递归程序得转化方法。 二.实验内容 (1)编程实现栈在两种存储结构中得基本操作(栈得初始化、判栈空、入栈、出栈等); (2)应用栈得基本操作,实现数制转换(任意进制); (3)编程实现队列在两种存储结构中得基本操作(队列得初始化、判队列空、入队列、出队列); (4)利用栈实现任一个表达式中得语法检查(括号得匹配)。 (5)利用栈实现表达式得求值。 注:(1)~(3)必做,(4)~(5)选做。 三.主要仪器设备及软件 (1)PC机 (2)Dev C++ ,Visual C++, VS2010等 四.实验主要流程、基本操作或核心代码、算法片段(该部分如不够填写,请另加附页)(1)编程实现栈在两种存储结构中得基本操作(栈得初始化、判栈空、入栈、出栈等); A、顺序储存: ?代码部分: //Main、cpp: #include"SStack、h" int main() { SqStack S; SElemType e;

int elect=1; InitStack(S); cout << "已经创建一个存放字符型得栈" << endl; while (elect) { Muse(); cin >> elect; cout << endl; switch (elect) { case 1: cout << "input data:"; cin >> e; Push(S, e); break; case 2: if(Pop(S, e)) {cout << e <<" is pop"<< endl; } else{cout<<"blank"<

(完整word版)顺序栈基本操作实验报告

数据结构实验三 课程数据结构实验名称顺序栈基本操作第页 专业班级学号 姓名 实验日期:年月日评分 一、实验目的 1.熟悉并能实现栈的定义和基本操作。 2.了解和掌握栈的应用。 二、实验要求 1.进行栈的基本操作时要注意栈"后进先出"的特性。 2.编写完整程序完成下面的实验内容并上机运行。 3.整理并上交实验报告。 三、实验内容 1.编写程序任意输入栈长度和栈中的元素值,构造一个顺序栈,对其进行清空、销毁、入栈、出栈以及取栈顶元素操作。 2.编写程序实现表达式求值,即验证某算术表达式的正确性,若正确,则计算该算术表达式的值。 主要功能描述如下: (1)从键盘上输入表达式。 (2)分析该表达式是否合法: ?a) 是数字,则判断该数字的合法性。若合法,则压入数据到堆栈中。 ?b) 是规定的运算符,则根据规则进行处理。在处理过程中,将计算该表达式的值。 ?c) 若是其它字符,则返回错误信息。 (3)若上述处理过程中没有发现错误,则认为该表达式合法,并打印处理结果。 程序中应主要包含下面几个功能函数: ?l void initstack():初始化堆栈 ?l int Make_str():语法检查并计算

?l int push_operate(int operate):将操作码压入堆栈 ?l int push_num(double num):将操作数压入堆栈 ?l int procede(int operate):处理操作码 ?l int change_opnd(int operate):将字符型操作码转换成优先级 ?l int push_opnd(int operate):将操作码压入堆栈 ?l int pop_opnd():将操作码弹出堆栈 ?l int caculate(int cur_opnd):简单计算+,-,*,/ ?l double pop_num():弹出操作数 四、实验步骤 (描述实验步骤及中间的结果或现象。在实验中做了什么事情,怎么做的,发生的现象和中间结果) 第一题: #include using namespace std; #define STACK_INIT_SIZE 100 //存储空间初始分配量 #define STACKINCREMENT 10 //存储空间分配增量 #define OVERFLOW -1 #define OK 1 #define NO -1 #define NULL 0 typedef int Status; typedef char SElemType; typedef struct { SElemType *base; //在栈构造之前和销毁之后,base的值为NULL SElemType *top; //栈顶指针 int stacksize; //当前已分配的存储空间,以元素为单位 } SqStack; Status Initstack(SqStack &S)//构造一个空栈S { S.base=(SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType)); if(!S.base) exit(OVERFLOW); S.top=S.base; S.stacksize= STACK_INIT_SIZE; return OK; }//InitStack Status StackEmpty(SqStack &S) { if(S.base==S.top)

数据结构实验—栈及其应用

《算法与数据结构》课程实验报告

一、实验目的 1.熟悉栈的特点(先进后出)及栈的基本操作,如入栈、出栈等,掌握栈 的基本操作在栈的顺序存储结构。 2.实现栈的顺序存储结构,通过实验深入理解栈的操作特点。 二、实验内容及要求 1.实现栈的存储结构及相关操作:进栈、出栈、取栈顶元素等。 2.使用该栈完成对一个字符串的逆序输出。 3.使用该栈完成判断表达式的括号是否匹配。 4.对算术表达式求值。 三、系统分析 (1)数据方面:该栈数据元素类型采用浮点型,在此基础上进行栈的基本操作,并可将栈中数据使用文本文档保存。在栈的应用中,采用的是存储字符元素类型的栈,并进行对字符的相关操作。 (2)功能方面:能实现栈的一些基本操作,主要包括: 1.进栈操作:若栈不满,则将元素x插入至栈的栈顶,若栈满则进行溢出 处理。 2.出栈操作:若栈不空,则函数返回该栈栈顶的元素,并且栈顶指针退1。 3.获取栈顶元素:若栈不空,则函数返回栈顶元素。 4.判断栈是否为空、判断栈是否满。 5.计算栈中元素个数:直接返回栈中元素个数。 6.清空栈内容:将栈顶指针赋为初始值。 7.保存数据:将栈中元素数据保存至文本文档中。 四、系统设计 (1)设计的主要思路 顺序栈可以采用顺序表作为其存储表示,为此,在顺序栈的声明中用顺序表定义它的存储空间。存放栈元素的数组的头指针为*elements,该数组最大能允许存放元素个数为maxSize,当前栈顶位置由数组下标指针top知识。并规定如果栈不空时,elements[0]为栈中第一个元素。由于实验中还需完成栈的相关应用,故使用两个菜单分别完成栈的基本操作与栈的应用调试。 (2)数据结构的设计 顺序栈定义为只允许在表的末端进行插入和删除的线性表。允许插入和删除的一端叫做栈顶,而不允许插入和删除的另一端叫做栈底。当栈中没有任何元素时则成为空战。即栈又被称为后进先出的线性表,故与线性表的相关操作类似,

实验三 栈的基本操作及应用

实验三栈的基本操作及应用 实验时间:第7周 实验目的:掌握栈的初始化、判空、出栈、入栈等基本操作 实验要求: 1.认真阅读和掌握教材上和本实验相关的算法。 2.上机将相关算法实现。 3.实现下面实验内容要求的功能,并输出程序的运行结果,结合程序进行分析。 实验内容: 利用栈的基本操作编程实现将任意一个十进制整数N转换为d进制整数。 算法提示: 1、定义栈的顺序存取结构(也可以用链栈) 2、分别定义栈的基本操作(初始化栈、判空、出栈、入栈等) 3、定义一个函数用来实现数制转换问题: 十进制整数N和d作为形参 初始化栈 只要N不为0重复做下列动作 将N%d入栈 N = N/d 只要栈不为空重复做下列动作 栈顶元素出栈 输出栈顶元素 注意:如果你完成上述任务还有时间,仔细分析、单步调试下面程序。目的有两个:一是消化、分析C++中的引用;二是练习使用VC中的调试器。 #include void fun(int &a)

{ a=8; } void main() { int x=1; //int &y=x; fun(x); //x=4; cout << x <

栈的类型定义与基本操作

栈的类型定义与基本操 作 Company number:【WTUT-WT88Y-W8BBGB-BWYTT-19998】

循环队链的出队 bool Dequeue( CSQueue &q, QElemType &e ) { int front; if( == 0 ) return false; front = ( + 1 - + MAXQSIZE ) % MAXQSIZE; e = [ front ]; --; return true; } 循环队链的入队 bool Enqueue( CSQueue &q, QElemType e ) { if( == MAXQSIZE ) return false; = ( + 1 ) % MAXQSIZE; [ ] = e; ++; return true; } 链队的入队 void Enqueue( LQueue &q, QElemType e ) { LQueuePtr p; p = new QNode; p->data = e; p->next = >next; >next = p; = p; } 链队的出队 bool Dequeue( LQueue &q, QElemType &e ) { LQueuePtr p; if( >next == ) return false; p = >next; e = p->next->data; >next = p->next;

delete p; return true; } 顺序栈的类型定义与基本操作: const StackInitSize=100; const StackInc=10; struct SStack { SElemType *base,*top; isited=false; for(i=1;i<=;i++) if(![i].visited) { visit[i].data); [i].visited=true; Enqueue(q,i); while(Dequeue(q,j)) for(p=[j].firstarc;p;p=p->nextarc) { k=p->adjvex; if(![k].visited) { visit(G>Vexs[k].data); [k].visited=true; Enqueue; } } } } 深度优先搜索遍历 void DFS(ALGraph &G, int i, void visit(VexType)) { int j; Arcptr p; visit[i].data); [i].visited=true; for(p=[i].firstarc ;p; p=p->nextarc) { J=p->adjvex; if(![j].visited) DFS(G,j,visit); } }

数据结构(C语言)栈的基本操作

实验名称栈的基本操作 实验目的 掌握栈这种抽象数据类型的特点及实现方法。 实验内容 从键盘读入若干个整数,建一个顺序栈或链式栈,并完成下列操作: (1)初始化栈; (2)判栈为空; (3)出栈; (4)入栈。 算法设计分析 (一)数据结构的定义 struct stackNode{ int data; struct stackNode *nextPtr; }; typedef struct stackNode listStact; typedef listStact *stackNodePtr; (二)总体设计 程序由主函数、入栈函数,出栈函数,删除函数判官是否为空函数和菜单函数组成。 (1)主函数:调用各个函数以实现相应功能

(三)各函数的详细设计: Function1: void instruct() //菜单 (1):使用菜单显示要进行的函数功能; Function2:void printStack(stackNodePtr sPtr) //输出栈 (1):利用if判断栈是否为空; (2):在else内套用while(头指针不为空条件循环)循环输出栈元素; Function3:void push(stackNodePtr *topPtr,int value //进栈 (1):建新的头指针; (2):申请空间; (3):利用if判断newPtr不为空时循环进栈 (4):把输入的value赋值给newPtr,在赋值给topPtr,再指向下一个位置; Function4:int pop(stackNodePtr*topPtr) //删除 (1):建新的头指针newPtr; (2):利用if判断newPtr是否为空,再删除元素。 (3):把topPtr等于newPtr,把头指针指向的数据赋值给topValue,输出要删除的数据值,头指针指向下一个位置,并清空newPtr; (4):完成上述步骤后,return toPvalue,返回;

栈的类型定义与基本操作

栈的类型定义与基本 操作 Revised on November 25, 2020

循环队链的出队 bool Dequeue( CSQueue &q, QElemType &e ) { int front; if( == 0 ) return false; front = ( + 1 - + MAXQSIZE ) % MAXQSIZE; e = [ front ]; --; return true; } 循环队链的入队 bool Enqueue( CSQueue &q, QElemType e ) { if( == MAXQSIZE ) return false; = ( + 1 ) % MAXQSIZE; [ ] = e; ++; return true; } 链队的入队 void Enqueue( LQueue &q, QElemType e ) { LQueuePtr p; p = new QNode; p->data = e; p->next = >next; >next = p; = p; } 链队的出队 bool Dequeue( LQueue &q, QElemType &e ) { LQueuePtr p; if( >next == ) return false; p = >next; e = p->next->data; >next = p->next;

delete p; return true; } 顺序栈的类型定义与基本操作: const StackInitSize=100; const StackInc=10; struct SStack { SElemType *base,*top; isited=false; for(i=1;i<=;i++) if(![i].visited) { visit[i].data); [i].visited=true; Enqueue(q,i); while(Dequeue(q,j)) for(p=[j].firstarc;p;p=p->nextarc) { k=p->adjvex; if(![k].visited) { visit(G>Vexs[k].data); [k].visited=true; Enqueue; } } } } 深度优先搜索遍历 void DFS(ALGraph &G, int i, void visit(VexType)) { int j; Arcptr p; visit[i].data); [i].visited=true; for(p=[i].firstarc ;p; p=p->nextarc) { J=p->adjvex; if(![j].visited) DFS(G,j,visit); } }

顺序栈的基本操作

上机实验报告 学院:计算机与信息技术学院 专业:计算机科学与技术(师范)课程名称:数据结构 实验题目:顺序栈的基本操作 班级序号:师范1班 学号:201421012731 学生姓名:邓雪 指导教师:杨红颖 完成时间:2015年12月25号

一、实验目的: 1.熟悉掌握栈的定义、结构及性质; 2.能够实现创建一个顺序栈,熟练实现入栈、出栈等栈的基本操作; 3.了解和掌握栈的应用。 二、实验环境: Microsoft Visual c++ 6.0 三、实验内容及要求: 栈是一种特殊的线性表,逻辑结构和线性表相同,只是其运算规则有更多的限制,故又称为受限的线性表。 建立顺序栈,实现如下功能: 1.建立一个顺序栈 2.输出栈 3.进栈 4.退栈 5.取栈顶元素 6.清空栈 7.判断栈是否为空 进行栈的基本操作时要注意栈"后进先出"的特性。 四、概要设计: 1、通过循环,由键盘输入一串数据。创建并初始化一个顺序栈。 2、编写实现相关功能函数,完成子函数模块如下。 3、调用子函数,实现菜单调用功能,完成顺序表的相关操作

五、代码: #include #include #define maxsize 64 typedef int datatype; //定义结构体 typedef struct { datatype data[maxsize]; int top; }seqstack; //建立顺序栈 seqstack *SET(seqstack *s) { int i; s=(seqstack*)malloc(sizeof(seqstack)); s->top=-1; printf("请输入顺序栈元素(整型,以0结束):"); scanf("%d",&i); do{ s->top++; s->data[s->top]=i; scanf("%d",&i); }while(i!=0); printf("顺序栈建立成功\n"); return s; } //清空栈 void SETNULL(seqstack *s) { s->top=-1;}

相关文档
最新文档