数据结构Word下载.docx
《数据结构Word下载.docx》由会员分享,可在线阅读,更多相关《数据结构Word下载.docx(72页珍藏版)》请在冰豆网上搜索。
stack=ARRAY[1..n]OFinteger;
PROCEDUREPUSH(VARs:
stack;
VARtop,x:
integer);
{入栈}
BEGIN
IFtop=nTHEN
writeln('
overflow'
)
ELSEBEGIN
top:
=top+1;
s[top]:
=x;
END;
PROCEDUREPOP(VARs:
VARy,top:
{出栈}
IFtop=0THENwriteln('
underflow'
)ELSEBEGIN
y:
=s[top];
=top-1;
END
对于出栈运算中的“下溢”,程序中仅给出了一个标志信息,而在实际应用中,下溢可用来作为控制程序转移的判断标志,是十分有用的。
对于入栈运算中的“上溢”,则是一种致命的错误,将使程序无法继续运行,所以要设法避免。
堆栈的数组模拟
十进制数N和其它d进制数的转换是实现计算的基本问题,解决方法很多,下面给出一中算法原理:
N=(Ndivd)×
d+Nmodd(其中div为整除运算,mod为求余运算)。
例如:
(1348)10=(2504)8运算过程如下:
N
Ndiv8
Nmod8
1348
168
4
21
2
5
9413
1、1、
填空:
(9413)10=()8=()16=()2
2、下面的程序实现这个转换过程,请补充完整。
'
数制转化程序【xoi00_07.pas】
programxoi00_07;
constsize=100;
vara:
array[1..size]ofinteger;
n,d,i,j:
integer;
begin
writeln;
write('
Pleaseenteranumber(N)base10:
);
readln(n);
pleaseenteranumber(d):
readln(d);
i:
=1;
repeat
a[i]:
=nmodd;
n:
=ndivd;
inc(i);
untiln=0;
forj:
=i-1downto1dowrite(a[j]:
5);
end.
2、火车站列车调度示意图如下,假设调度站两侧的轨道为单向行驶轨道。
如果进站的车厢序列为123,则可能的出战车厢序列是什么?
2、2、
如果进展进站的车厢序列为123456,问能否得到135426和435612的出站序列。
栈的用途极为广泛,在源程序编译中表达式的计算、过程的嵌套调用和递归调用等都要用到栈,下面以表达式计算为例子加以说明。
源程序编译中,若要把一个含有表达式的赋值语句翻译成正确求值的机器语言,首先应正确地解释表达式。
例如,对赋值语句
X:
=4+8×
2-3;
(式11.1)
其正确的计算结果应该是17,但若在编译程序中简单地按自左向右扫描的原则进行计算,则为:
X=12×
2-3=24-3=21
这结果显然是错误的。
因此,为了使编译程序能够正确地求值,必须事先规定求值的顺序和规则。
通常采用运算符优先数法。
一般表达式中会遇到操作数、运算符和语句结束符等,以算术运算符为例,对每种运算赋予一个优先数,如:
运算符:
×
÷
+ -
优先数:
2 2 1 1
(语句结束符“;
”的优先数为零)
在运算过程中,优先数高的运算符应先进行运算(但遇到括号时,应另作处理)。
按这样的规定,对式(11.1)自左向右进行运算时,其计算顺序就被唯一地确定下来了。
计算顺序确定后,在对表达式进行编译时,一般设立两个栈,一个称为运算符栈(OPS),另一个称为操作数栈(OVS),以便分别存放表达式中的运算符和操作数。
编译程序自左向右扫描表达式直至语句结束,其处理原则是:
①凡遇到操作数,一律进入操作数栈;
②当遇到运算符时,则将运算符的优先数与运算符栈中的栈顶元素的优先数相比较;
若该运算符的优先数大,则进栈;
反之,则取出栈顶的运算符,并在操作数栈中连续取出两个栈顶元素作为运算对象进行运算,并将运算结果存入操作数栈,然后继续比较该运算符与栈顶元素的优先数。
例如式(11.1)中,当扫描到“+”和“×
”时都要将运算符入栈。
接着扫描到“-”号,其优先数小于乘号所以乘号退栈,并执行8×
2,将结果16再存入操作数栈。
再将“-”号的优先数与运算符栈的栈顶元素“+”号的优先数相比较,两者相等,所以再将加号退栈,进行4+16,结果为20,再入栈,接着,由于运算栈已空,所以减号入栈。
当扫描到“3”时,操作数入栈。
当扫描到“;
”时,其优先数最低,所以减号退栈并执行20-3,结果为17并入栈。
因已扫描到语句结束符,所以表达式的求值结束,结果为17。
例题模拟计算机处理算术表达式过程。
从键盘上输入算术表达式串(只含+、-、×
、÷
运算符,充许含括号),输出算术表达式的值。
设输入的表达式串是合法的。
分析:
建立两个栈,一个是操作数栈(number),一个是运算符栈(symbol),根据运算符的优先级对两个栈进行相应的操作。
源程序
programex11_4;
const
max=100;
var
number:
array[0..max]ofinteger;
symbol:
array[1..max]ofchar;
s,t:
string;
i,p,j,code:
integer;
procedurepush;
{算符入栈运算}
inc(p);
symbol[p]:
=s[i];
end;
procedurepop;
{运算符栈顶元素出栈,并取出操作数栈元素完成相应的运算}
dec(p);
casesymbol[p+1]of
'
+'
:
inc(number[p],number[p+1]);
-'
dec(number[p],number[p+1]);
*'
number[p]:
=number[p]*number[p+1];
/'
=number[p]divnumber[p+1];
end;
functioncan:
boolean;
{判断运算符的优先级别,建立标志函数}
can:
=true;
if(s[i]in['
'
])and(symbol[p]<
>
('
)thenexit;
])and(symbol[p]in['
])thenexit;
=false;
String:
readln(s);
s:
='
+s+'
)'
;
i:
=1;
p:
=0;
whilei<
=length(s)do
begin
whiles[i]='
do{左括号处理}
push;
j:
=i;
repeat{取数入操作数栈}
until(s[i]<
0'
)or(s[i]>
9'
t:
=copy(s,j,i-j);
val(t,number[p],code);
ifs[i]='
then{右括号处理}
whilesymbol[p]<
do
pop;
=number[p+1];
end
else
begin{根据标志函数值作运算符入栈或出栈运算处理}
whilecando
until(i>
length(s))or(s[i-1]<
Result='
number[0]);
readln;
练习题:
1、读入一英文句子,单词之间用空格或逗号隔开,统计其中单词个数,并输出各个字母出现的频率。
(句子末尾不一定用"
."
结束)如果含有其他的字符,则只要求输出错误信息及错误类型。
含有大写字母错误类型error1
数字(0-9)错误类型error2
其他非法字符错误类型error3
如输入:
Itis12!
输出:
error123
输入:
iam,astudent
4
编码解码:
从键盘输入一个英文句子,设计一个编码、解码程序。
(string)
编码过程:
先键入一个正整数N(1<
=N<
=26)。
这个N决定了转换关系。
例如当N=1,输入的句子为ABCXYZ时,则其转换码为ABCXYZ不变。
当N=2时,其转换码为BCDYZA,其它的非字母字符不变。
为使编码较于破译,将转换码的信息自左而右两两交换,若最后仅剩单个字符则不换。
然后,将一开始表示转换关系的N根据ascii表序号化成大写字母放在最前面。
如:
abcABCxyzXYZ-/,1.n=3
cdeCDEzabZAB-/,1.{根据N的值转换}
dcCeEDazZbBA/-1,.{两两交换}
CdcCeEDazZbBA/-1,.{最后编码}
解码过程为编码的逆过程。
4、计算器的改良【第三届全国青少年信息学奥林匹克分区联赛复赛试题普及组题一】
〖问题描述〗
NCL是一家专门从事计算器改良与升级的实验室。
最近该实验室收到了某公司所委托的一个任务:
需要在该公司某型号的计算器上加上解一元一次方程的功能。
实验室将这个任务交组了一个刚进入的新手ZL先生。
为了很好的完成这个任务,ZL先生首先研究了一些一元一次方程的实例:
4+3X=8
6a-5+1=2-2
-5+12Y=0
ZL先生被告知:
在计算器上键入的一个一元一次方程中,只包含整数、小写字母入+、-、=这三个数学符号(当然,“-”既可当减号也可当负号)。
方程中并没有括号,也没有除号,方程中的字母表示末知数。
〖问题求解〗
编写程序,解输入的一元一次方程,将解方程的结果(精确到小数点后三位)输出至屏幕。
键入的一元一次方程均合法,且有唯一的实数解。
〖样例〗
6a-5+1=2-2a
a=0.750
在
(一)中,我们谈了"
栈"
的应用,下面我们谈谈队列,队列是限定在一端进行插入,另一端进行删除和特殊线性表。
正象排列买东西,排在前面的人买完东西后离开队伍(删除),而后来的人总是排在队伍未尾(插入)。
通常把队列的删除和插入分别称为出队和入队。
允许出队的一端称为队头,允许入队的一端称为队尾。
所有需要进队的数据项,只能从队尾进入,队列中的数据项只能从队头离去。
由于总是先入队的元素先出队(先排队的人先买完东西),这种表也称为先进先表(FIFO)表。
队列可以用数组Q[1…m]来存储,数组的上界m即是队列所容许的最大容量。
在队列的运算中需设两个指针:
head:
队头指针,指向实际队头元素的前一个位置tall:
队尾指针,指向实际队尾元素所在的位置一般情况下,两个指针的初值设为0,这时队列为空,没有元素。
图1(a)画出了一个由6个元素构成的队列,数组定义Q[1…10]。
Q(i)i=3,4,5,6,7,8头指针head=2,尾指针tail=8。
队列中拥有的元素个数为:
L=tail-head现要让排头的元素出队,则需将头指针加1。
即head=head+1这时头指针向上移动一个位置,指向Q(3),表示Q(3)已出队。
见图1(b)。
如果想让一个新元素入队,则需尾指针向上移动一个位置。
即tail=tail+1这时Q(9)入队,见图1(c)。
当队尾已经处理在最上面时,即tail=10,见图1(d),如果还要执行入队操作,则要发生"
上溢"
,但实际上队列中还有三个空位置,所以这种溢出称为"
假溢出"
。
克服假溢出的方法有两种。
一种是将队列中的所有元素均向低地址区移动,显然这种方法是很浪费时间的;
另一种方法是将数组存储区看成是一个首尾相接的环形区域。
当存放到n地址后,下一个地址就"
翻转"
为1。
在结构上采用这种技巧来存储的队列称为循环队列,见图2
循环队的入队算法如下:
1、tail=tail+1;
2、若tail=n+1,则tail=1;
3、若head=tail尾指针与头指针重合了,表示元素已装满队列,则作上溢出错处理;
4、否则,Q(tail)=X,结束(X为新入出元素)。
队列和栈一样,有着非常广泛的应用。
考虑一个分时系统,如果一台计算机联有四个终端,即允许四个用户同时使用这一台计算机。
那么,计算机系统必须设立一个队列,用以管理各终端用户使用CPU的请求。
当某个用户要求使用CPU时,相应的终端代号就入队(插入队尾),而队头的终端用户则是CPU当前服务的对象。
我们考虑最简单的情况,
对于当前用户(队头),系统每次分配一个为时间片的时间间隔,在一个时间片内,如果当前用户的作业没有结束,则该终端用户的代号出队后重新入队,插入队尾,等待下一次CPU服务。
如果某个用户的作业运行结束,则先退出,出队后不再入队,
整个运行过程就是各终端代号不断地入队、出队,CPU轮流地为n(n≤4)个终端用户服务。
由于计算机的运行速度极快,所以,对于每个终端用户来说,似乎计算机是单独在为其服务。
和线性表一样,栈和队可以采用链表存储结构,当要实现多个栈共享内存或多个队共享内存时,选择链式分配结构则更为合适。
例1求两个一元多项式的和。
输入多项式方式为,多项式项数,每项系数和指数,按指数从大到小的顺序输入。
分析
多项式的算术运算是表处理的一个经典问题。
建立两张表a、b分别存放两个多项式的内容,建立表指针ta、tb,指向表a和表b的元素,根据表a、b元素中的指数大小合并输出。
1、比较ta、tb指向元素的大小,若ta的指数大于tb的指数,输出ta元素,改变指针ta;
2、若ta的指数小于tb的指数,输出tb元素,改变指针tb;
3、若ta的指数等于tb的指数,ta、tb元素的系数相加输出,同时改变指针ta和tb;
4、若有一表取空,则输出另一表剩余的内容。
源程序一:
多项式相加的顺序表实现
programex11_5a;
type
node=record
zhi,xi:
ar=array[1..1000]ofnode;
a,b:
ar;
ta,tb,n:
One:
{输入第一个多项式的系数和指数}
forta:
=ndownto1do
readln(a[ta].xi,a[ta].zhi);
ta:
=n;
Two:
{输入第二个多项式的系数和指数}
fortb:
readln(b[tb].xi,b[tb].zhi);
tb:
Resultis'
while(ta>
0)and(tb>
0)do{当两个表均不空时}
begin{比较两表指针指向的项指数,输出指数小的项系数和指数,同时改变该表指针}
ifa[ta].zhi>
b[tb].zhithen
ifa[ta].xi<
0thenwrite(#8'
#8);
write(a[ta].xi,'
x'
a[ta].zhi,'
dec(ta);
ifa[ta].zhi
ifb[tb].xi<
write(b[tb].xi,'
b[tb].zhi,'
dec(tb);
else
begin{若两表指针指向的项指数相等,则两系数相加输出,两表指针同时改变}
ifb[tb].xi+a[ta].xi<
0then
write(b[tb].xi+a[ta].xi,'
whileta>
0do{若有一表空,则输出另一表的剩余项}
whiletb>
0do
writeln(#8'
readln;
源程序二:
多项式相加的链表实现
programex11_5b;
link=^node;
nxt:
link;
procedurecreatefifo(varc:
link);
{建立多项式系数、指数链表}
p:
new(p);
readln(p^.xi,p^.zhi);
c:
=p;
fori:
=1ton-1do
new(p^.nxt);
=p^.nxt;
p^.nxt:
=nil;
createfifo(a);
createfifo(b);
while(a<
nil)and(b<
nil)do
ifa^.zhi>
b^.zhithen
ifa^.xi<
write(a^.xi,'
a^.zhi,'
a:
=a^.nxt;
ifa^.zhi
ifb^.xi<
write(b^.xi,'
b^.zhi,'
b:
=b^.nxt;
ifb^.xi+a^.xi<
write(b^.xi+a^.xi,'
whilea<
nildo
whileb<
3.队列的应用:
例:
设有一个表,记为L=(a1,a2,a3,...,an),其中
L——表名
a1,a2,a3,..,an——表中元素。
当ai为数值时表示元素,当ai为大写字母时,表示另一个表,但不能循环定义。
例如,下列的定义是合法的(约定L是第一个表的表名):
L=(3.4,3,4,K,8,0,8)
K=(15,5,8,P,9,4)
P=(4,7,8,9)
程序要求:
当全部表给出后,示出所有元素的最大元素。
思路: