在趣谈数据结构(一)中,我们谈了"栈"的应用,下面我给出一道问题,大家看看能否用栈的知识来解决? 问题:化学中原子的表示是由一个大写字母或一个大写字母加一个小写字母来表示的。原子团是由原子或更小的原子团参与构成的,原子团的表达式是由左、右圆括号括起来的,NH4原子团的表达式为(NH4)。但当原子团只出现一次,则原子团表达式两端的圆括号可以省略不写。分子是由原子组成,分子式是原子式的组合。例如NH4和co3是两种不同的原子团,碳酸铵分子中有两个NH4原子团和一个co3原子团,故该分子的分子式可以书写成两种形式:(NH4)2CO3、(NH4)2(CO3)。
分子量就是分子的质量,是构成该分子的所有的原子(包括构成该分子的原子团中的各个原子)的质量总和。 例如N的原子量为14,H的原子量为1,C的原子量为12,O的原子量16,因此(NH4)2CO3的分子量为(14+l*4)*2+12+16*3=96。
现已知各原子的原子量,求出给定的各个分子式所对应的分子量。
这一讲,我们就来谈谈"栈"。
队列是限定在一端进行插入,另一端进行删除和特殊线性表。正象排列买东西,排在前面的人买完东西后离开队伍(删除),而后来的人总是排在队伍未尾(插入)。通常把队列的删除和插入分别称为出队和入队。允许出队的一端称为队头,允许入队的一端称为队尾。所有需要进队的数据项,只能从队尾进入,队列中的数据项只能从队头离去。由于总是先入队的元素先出队(先排队的人先买完东西),这种表也称为先进先表(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、若有一表取空,则输出另一表剩余的内容。
源程序一:多项式相加的顺序表实现
program ex11_5a; type node=record zhi,xi:integer; end; ar=array[1..1000] of node; var a,b:ar; ta,tb,n:integer; begin write('One : '); readln(n);{输入第一个多项式的系数和指数} for ta:=n downto 1 do readln(a[ta].xi,a[ta].zhi); ta:=n; write('Two : '); readln(n);{输入第二个多项式的系数和指数} for tb:=n downto 1 do readln(b[tb].xi,b[tb].zhi); tb:=n; write('Result is '); while (ta>0) and (tb>0) do {当两个表均不空时} begin {比较两表指针指向的项指数,输出指数小的项系数和指数, 同时改变该表指针} if a[ta].zhi>b[tb].zhi then begin if a[ta].xi<0 then write(#8' '#8); write(a[ta].xi,'x',a[ta].zhi,'+'); dec(ta); end else if a[ta].zhi<b[tb].zhi then begin if b[tb].xi<0 then write(#8' '#8); write(b[tb].xi,'x',b[tb].zhi,'+'); dec(tb); end else begin {若两表指针指向的项指数相等,则两系数相加输出, 两表指针同时改变} if b[tb].xi+a[ta].xi<>0 then begin if b[tb].xi+a[ta].xi<0 then write(#8' '#8); write(b[tb].xi+a[ta].xi,'x',b[tb].zhi,'+'); end; dec(ta); dec(tb); end; end; while ta>0 do {若有一表空,则输出另一表的剩余项} begin if a[ta].xi<0 then write(#8' '#8); write(a[ta].xi,'x',a[ta].zhi,'+'); dec(ta); end; while tb>0 do begin if b[tb].xi<0 then write(#8' '#8); write(b[tb].xi,'x',b[tb].zhi,'+'); dec(tb); end; writeln(#8' '#8); readln; end.
源程序二:多项式相加的链表实现
program ex11_5b; type link=^node; node=record zhi,xi:integer; nxt:link; end; var a,b:link; n:integer; procedure createfifo(var c:link);{建立多项式系数、指数链表} var p:link; i:integer; begin new(p); readln(p^.xi,p^.zhi); c:=p; for i:=1 to n-1 do begin new(p^.nxt);p:=p^.nxt;readln(p^.xi,p^.zhi); end; p^.nxt:=nil; end; begin write('One : '); readln(n); createfifo(a); write('Two : '); readln(n); createfifo(b); write('Result is '); while (a<>nil) and (b<>nil) do begin if a^.zhi>b^.zhi then begin if a^.xi<0 then write(#8' '#8); write(a^.xi,'x',a^.zhi,'+'); a:=a^.nxt; end else if a^.zhi<b^.zhi then begin if b^.xi<0 then write(#8' '#8); write(b^.xi,'x',b^.zhi,'+'); b:=b^.nxt; end else begin if b^.xi+a^.xi<>0 then begin if b^.xi+a^.xi<0 then write(#8' '#8); write(b^.xi+a^.xi,'x',b^.zhi,'+'); end; b:=b^.nxt; a:=a^.nxt; end; end; while a<>nil do begin if a^.xi<0 then write(#8' '#8); write(a^.xi,'x',a^.zhi,'+'); a:=a^.nxt; end; while b<>nil do begin if b^.xi<0 then write(#8' '#8); write(b^.xi,'x',b^.zhi,'+'); b:=b^.nxt; end; writeln(#8' '#8); readln; end.
| 趣谈数据结构(三) |
|
|
在 趣谈数据结构(二)中,我们介绍了"队"及"队"的应用,在这一讲中,我们将再通过两道例题,进一步的了解的学习"队"的灵活使用。
例2、 设有n个人依次围成一圈,从第1个人开始报数,数到第m个人出列,然后从出列的下一个人开始报数,数到第m个人又出列,…,如此反复到所有的人全部出列为止。设n个人的编号分别为1,2,…,n,打印出出列的顺序。
分析 本题我们可以用数组建立标志位等方法求解,但如果用上数据结构中循环链的思想,则更贴切题意,解题效率更高。 n人围成一圈,把一人看成一个结点,n人之间的关系采用链接方式,即每一结点有一个前继结点和一个后继结点,每一个结点有一个指针指向下一个结点,最后一个结点指针指向第一个结点。这就是单循环链的数据结构。 当m人出列时,将m结点的前继结点指针指向m结点的后继结点指针,即把m结点驱出循环链。
1、 建立循环链表。 当用数组实现本题链式结构时,数组a[i]作为"指针"变量来使用,a[i]存放下一个结点的位置。设立指针j指向当前结点,则移动结点过程为j:=a[j],当数到m时,m结点出链,则a[j]:=a[a[j]]。 当直接用链来实现时,则比较直观,每个结点有两个域:一个数值域,一个指针域,当数到m时,m出链,将m结点的前继结点指针指向其后继结点; 2、 设立指针,指向当前结点,设立计数器,计数数到多少人; 3、 沿链移动指针,每移动一个结点,计数器值加1,当计数器值为m时,则m结点出链。计数器值置为1; 4、 重复3、直到n个结点均出链为止。
源程序一: 用数组实现链式结构 program ex11-6a; const n=14;m=4;{设有10个人,报到4的人出列} var a:array[1..n] of integer; i,j,k,p:integer; begin for i:=1 to n-1 do a[i]:=i+1;{建立链表} a[n]:=1;j:=n;k:=1;p:=0;{第n人指向第1人,并置初始} repeat j:=a[j];k:=k+1;{报数,计数器加1} if k=m then {数到m,m人出队,计数器置1} begin write(a[j]:4);p:=p+1;a[j]:=a[a[j]];k:=1; end until p=n;{直到n个人均出队为止} end.
源程序二: 单链环实现 program ex11-6b; type pointer=^node; node=record val:integer; link:pointer end; var ptr,ptb:pointer; i,j,n,m:integer; begin readln(n,m); new(ptb);ptb^.val:=1;ptr:=ptb;{申请第1个结点} for i:=2 to n do begin new(ptr^.link);ptr:=ptr^.link; {申请第2到n结点} ptr^.val:=i; end; ptr^.link:=ptb;j:=0;{第n结点指针指向第1个结点} repeat i:=1; repeat {报数,报到m人出列} ptr:=ptb;ptb:=ptb^.link;i:=i+1; until i=m; write(ptb^.val:2); ptb:=ptb^.link;ptr^.link:=ptb;j:=j+1;{将m结点驱出链表} until j=n-1;{直到n个人均出队为止} writeln(ptb^.val:4) end.
例3、 一矩形阵列由数字0到9组成,数字1到9代表细胞,细胞的定义为沿细胞数字上下左右还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。 如:阵列 0234500067 1034560500 2045600671 0000000089 有4个细胞。
算法步骤:
1. 从文件中读入m*n矩阵阵列,将其转换为boolean矩阵存入bz数组中; 2. 沿bz数组矩阵从上到下,从左到右,找到遇到的第一个细胞; 3. 将细胞的位置入队h,并沿其上、下、左、右四个方向上的细胞位置入队,入队后的位置bz数组置为FLASE; 4. 将h队的队头出队,沿其上、下、左、右四个方向上的细胞位置入队,入队后的位置bz数组置为FLASE; 5. 重复4,直至h队空为止,则此时找出了一个细胞; 6. 重复2,直至矩阵找不到细胞; 7. 输出找到的细胞数。
program xibao; const dx:array[1..4] of -1..1=(-1,0,1,0); dy:array[1..4] of -1..1=(0,1,0,-1); var int:text; name,s:string; pic:array[1..50,1..79] of byte; bz:array[1..50,1..79] of boolean; m,n,i,j,num:integer; h:array[1..4000,1..2] of byte; procedure doing(p,q:integer); var i,t,w,x,y:integer; begin inc(num);bz[p,q]:=false; t:=1;w:=1;h[1,1]:=p;h[1,2]:=q;{遇到的第一个细胞入队} repeat for i:=1 to 4 do{沿细胞的上下左右四个方向搜索细胞} begin x:=h[t,1]+dx[i];y:=h[t,2]+dy[i]; if (x>0) and (x<=m) and (y>0) and (y<=n) and bz[x,y] then begin inc(w);h[w,1]:=x;h[w,2]:=y;bz[x,y]:=false;end;{为细胞的入队} end; inc(t);{队头指针加1} until t>w;{直至队空为止} end; begin fillchar(bz,sizeof(bz),true);num:=0; write('input file:');readln(name); assign(int,name);reset(int); readln(int,m,n); for i:=1 to m do begin readln(int,s); for j:=1 to n do begin pic[i,j]:=ord(s[j])-ord('0'); if pic[i,j]=0 then bz[i,j]:=false; end; end; close(int); for i:=1 to m do for j:=1 to n do if bz[i,j] then doing(i,j);{在矩阵中寻找细胞} writeln('NUMBER of cells=',num);readln; end. | |