pascal指针与链表.docx
- 文档编号:12358428
- 上传时间:2023-04-18
- 格式:DOCX
- 页数:19
- 大小:66.05KB
pascal指针与链表.docx
《pascal指针与链表.docx》由会员分享,可在线阅读,更多相关《pascal指针与链表.docx(19页珍藏版)》请在冰豆网上搜索。
pascal指针与链表
指针与链表
前面介绍的各种简单类型的数据和构造类型的数据属于静态数据。
在程序中,这些类型
的变量一经说明,就在内存中占有固定的存储单元,直到该程序结束。
程序设计中,使用静态数据结构可以解决不少实际问题,但也有不便之处。
如建立一个
大小未定的姓名表,随时要在姓名表中插入或删除一个或几个数据。
而用新的数据类型──
指针类型。
通过指针变量,可以在程序的执行过程中动态地建立变量,它的个数不再受限制,
可方便地高效地增加或删除若干数据。
一、指针的定义及操作
(一)指针类型和指针变量
在pascal中,指针变量(也称动态变量)存放某个存储单元的地址;也就是说,指针变量
指示某个存储单元。
指针类型的格式为:
^基类型
说明:
①一个指针只能指示某一种类型数据的存储单元,这种数据类型就是指针的基类
型。
基类型可以是除指针、文件外的所有类型。
例如,下列说明:
typepointer=^Integer;
varp1,p2:
pointer;
定义了两个指针变量p1和p2,这两个指针可以指示一个整型存储单元(即p1、p2中存放的是某存储单元的地址,而该存储单元恰好能存放一个整型数据)。
②和其它类型变量一样,也可以在var区直接定义指针型变量。
例如:
vara:
^real;b:
^boolean;
又如:
typeperson=record
name:
string[20];
sex:
(male,female);
age:
1..100
end;
varpts:
^person;
③pascal规定所有类型都必须先定义后使用,但只有在定义指针类型时可以例外,如下
列定义是合法的:
typepointer=^rec;
rec=record
a:
integer;
b:
char
end;
(二)开辟和释放动态存储单元
1、开辟动态存储单元
在pascal中,指针变量的值一般是通过系统分配的,开辟一个动态存储单元必须调用标
准过程new。
new过程的调用的一般格式:
New(指针变量)
功能:
开辟一个存储单元,此单元能存放的数据的类型正好是指针的基类型,并把此存
储单元的地址赋给指针变量。
说明:
①这实际上是给指针变量赋初值的基本方法。
例如,设有说明:
varp:
^Integer;
这只定义了P是一个指示整型存储单元的指针变量,但这个单元尚未开辟,或者说P中尚未有值(某存储单元的首地址)。
当程序中执行了语句new(p)才给p赋值,即在内存中开辟(分配)一个整型变量存储单元,并把此单元的地址放在变量p中。
示意如下图:
(a)编译时给 (b)执行New(p)后 (c)(b)的简略表示
p分配空间 生成新单元
?
表示值不定 新单元的地址为XXXX
内存单元示意图
②一个指针变量只能存放一个地址。
如再一次执行New(p)语句,将在内存中开辟另外一个新的整型变量存储单元,并把此新单元的地址放在p中,从而丢失了原存储单元的地址。
③当不再使用p当前所指的存储单元时,可以通过标准过程Dispose释放该存储单元。
⒉释放动态存储单元
dispose语句的一般格式:
dispose(指针变量)
功能:
释放指针所指向的存储单元,使指针变量的值无定义。
(三)动态存储单元的引用
在给一个指针变量赋以某存储单元的地址后,就可以使用这个存储单元。
引用动态存储单元一般格式:
<指针变量>^
说明:
①在用New过程给指针变量开辟了一个它所指向的存储单元后,要使用此存储单元的唯一方法是利用该指针。
②对动态存储单元所能进行的操作是该类型(指针的基类型)所允许的全部操作。
例1设有下列说明:
varp:
^integer;i:
integer;
画出执行下列操作后的内存示意图:
New(p);P^:
=4;i:
=p^;
解:
如下图所示。
(a)编译时(b)执行New语句 (c)执行P^:
=4 (d)执行i:
=P^
分配存储
单元
内存单元示意图
(四)对指针变量的操作
前已述及,对指针所指向的变量(如P^)可以进行指针的基类型所允许的 全部操作。
对指针变量本身,除可用New、Dispose过程外,尚允许下列操作:
⒈具有同一基类型的指针变量之间相互赋值
例2设有下列说明与程序段:
varp1,p2,p3:
^integer;
begin
New(P1);New(P2);New(P3);
P1:
=P2;P2:
=P3;
end;
2、可以给指针变量赋nil值
nil是PASCAL的关键字,它表示指针的值为"空"。
例如,执行:
p1:
=ni1后,p1的值是有定义的,但p1不指向任何存储单元。
3、可以对指针变量进行相等或不相等的比较运算
在实际应用中,通常可以在指针变量之间,或指针变量与nil之间进行相等(=)或不相等(<>=的比较,比较的结果为布尔量。
4.需要注意之处
1、P与P^的区别
P是指向该动态变量的指针变量名,P^则称为动态变量或标志变量。
P的值是P^的首地址,P^的值为与基类型相同的一个值。
2、定义后及时分配存储单元
定义了一个指针变量后,并没有为该指针分配动态存储单元,此时的P的值无定义,调用P^则会产生运行错误。
若想使该指针可用,可以对指针赋值,也可以通过NEW()过程分配存储单元。
3、使用后及时收回存储单元
指针使用后,不会自动归还占用的存储空间,应及时使用DISPOSE()过程来释放P^所占用的存储单元,以免浪费有限的存储空间.
例3输入两个整数,按从小到大打印出来。
分析:
不用指针类型可以很方便地编程,但为了示例指针的用法,我们利用指针类型。
定义一个过程swap用以交换两个指针的值。
源程序如下:
Typepointer=^integer;
varp1,p2:
pointer;
procedureswap(varq1,q2:
pointer);
varq:
pointer;
begin
q:
=q1;
q1:
=q2;
q2:
=q;
end;
begin
new(p1);new(p2);
write('Input2data:
');readln(pq^,p2^);
ifp1^>p2^thenswap(p1,p2);
writeln('Output2data:
',p1^:
4,p2^:
4);
end.
二、链表结构
设有一批整数(12,56,45,86,77,……,),如何存放呢?
当然我们可以选择以前学过的数组类型。
但是,在使用数组前必须确定数组元素的个数。
如果把数组定义得大了,就会有大量空闲存储单元,定义得小了,又会在运行中发生下标越界的错误,这是静态存储分配的局限性。
利用本章介绍的指针类型可以构造一个简单而实用的动态存储分配结构――链表结构。
下图是一个简单链表结构示意图:
其中:
①每个框表示链表的一个元素,称为结点。
②框的顶部表示了该存储单元的地址(当然,这里的地址是假想的)。
③每个结点包含两个域:
一个域存放整数,称为数据域,另一个域存放下一个结点(称为该结点的后继结点,相应地,该结点为后继结点的前趋结点)的地址。
④链表的第一个结点称为表头,最后一个结点表尾,称为指针域;
⑤指向表头的指针head称为头指针(当head为nil时,称为空链表),在这个指针变量中存放了表头的地址。
⑥在表尾结点中,由指针域不指向任何结点,一般放入nil。
(一)链表的基本结构
由上图可以看出:
①链表中的每个结点至少应该包含两个域;一是数据域,一是指针域。
因此,每个结点都是一个记录类型,指针的基类型也正是这个记录类型。
因此,head可以这样定义:
typepointer=^rec;
rec=record
data:
integer;
next:
pointer;
end;
varhead:
pointer;
②相邻结点的地址不一定是连续的。
整个链表是通过指针来顺序访问的,一旦失去了一个指针值,后面的元素将全部丢失。
③与数组结构相比,使用链表结构时;可根据需要采用适当的操作步骤使链表加长或缩短,而使存储分配具有一定的灵活性。
这是链表结构的优点。
④与数组结构相比,数组元素的引用比较简单,直接用"数组名[下标]"即可,因为数组元素占用连续的存储单元,而引用链表元素的操作却比较复杂。
(二)单向链表的基本操作
上图所示的链表称为单向链表。
下面我们通过一些例题来说明对单向链表的基本操作,并假设类型说明如前所述。
例6编写一个过程,将读入的一串整数存入链表,并统计整数的个数。
分析:
过程的输入为一串整数,这在执行部分用读语句完成。
过程的输出有两个:
一是链表的头指针,一是整数的个数,这两个输出可以用变量形参来实现。
由于不知道整数的个数,我们用一个特殊的9999作为结束标记。
过程如下:
procedurecreat(varh:
pointer;varn:
integer);
varp,q:
pointer;x:
integer;
begin
n:
=0;h:
=nil;read(x);
whilex<>9999do
begin
New(p);
n:
=n+1;p^.data:
=x;
ifn=1thenh:
=p
elseq^.next:
=p;
q:
=p;read(x)
end;
ifh<>nilthenq^.next:
=nil;
Dispose(p);
end;
例7编一过程打印链表head中的所有整数,5个一行。
分析:
设置一个工作指针P,从头结点顺次移到尾结点,每移一次打印一个数据。
过程如下:
procedureprint(head:
pointer);
varp:
pointer;n:
integer;
begin
n:
=0;p:
=head;
whilep<>nildo
begin
write(p^.data:
8);n:
=n+1;
ifnmod5=0thenwriteln;
p:
=p^.next;
end;
writeln;
end;
(三)链表结点的插入与删除
链表由于使用指针来连接,因而提供了更多了灵活性,可以插入删除任何一个成分。
设有如下定义:
typepointer=^rec;
rec=record
data:
integer;
next:
pointer
end;
varhead:
pointer;
⒈结点的插入
如下图所示,要在P结点和Q结点之间插入一个结点m,其操作如下:
只要作如下操作即可:
New(m);
read(m^.data);
m^.next:
=q;
p^.next:
=m;
例8设链表head中的数据是按从小到大顺序存放的,在链表中插入一个数,使链表仍有序。
分析:
显然,应分两步:
查找、插入。
设po指向要插入的结点,若仅知道po应插在p之前(作为p的前趋结点)是无法插入的,应同时知道p的前趋结点地址q。
当然,如果插在链表原头结点这前或原链表为空表或插在原尾结点之后,则插入时又必须作特殊处理。
过程如下:
procedureinserting(varhead:
pointer;x:
integer);
varpo,p,q:
pointer;
begin
new(po);po^.data:
=x;
p:
=head;
ifhead=nil{原表为空表}
thenbegin
head:
=po;po^.next:
=nil;
end
elsebegin
while(p^.data
begin
q:
=p;p:
=p^.next
end;
ifp^.data>=x{不是插在原尾结点之后}
thenbegin
ifhead=pthenhead:
=po
elseq^.next:
=po;
po^.next:
=p
end
elsebegin
po^.next:
=po;
po^.next:
=nil
end;
end;
end;
⒉结点的删除
如下图所示,要在删除结点P的操作如下:
要删除结点P,则只要将其前趋结点的指针域指向P的后继结点即可。
q^.next:
=p^.next;
dispose(p);
例9将链表head中值为X的第一个结点删除
分析:
有三种情况存在:
头结点的值为X;除头结点外的某个结点值为X;无值为X的结点。
为将前两种情况统一起来,我们在头结点之前添加一个值不为X的哨兵结点。
算法分两步:
查找、删除。
过程如下:
proceduredeleteing(varhead:
pointer;x:
integer);
varp,q:
pointer;
begin
New(p);p^.data:
=x-1;p^.next:
=head;
head:
=p;{以上为添加哨兵结点}
while(x<>p^.data)and(p^.next<>nil)do
begin
q:
=p;
p:
=p^.next
end;
ifx=p^.data{存在值为X的结点}
thenq^.next:
=p^.next
elsewriteln('NOtfound!
');
head:
=head^.next{删除哨兵}
end;
(四)环形链表结构
在单向链表中,表尾结点的指针为空。
如果让表尾结点的指针域指向表头结点,则称为单向环形链表,简称单链环。
如图所示。
单链环示意图
(五)双向链表结构
单链表中,每个结点只有一个指向其后继结点的指针域。
如果每个结点不仅有一个指向其后继结点的指针域,还有一个指向其前趋的指针域,则这种链表称为双向链表。
如图所示。
双向链表示意图
可用如下定义一个数据域为整型的双向链表:
typepointer=^node;
node=record
prev:
pointer;
data:
integer;
next:
pointer;
end;
对双向链表的插入、删除特别方便。
与单向链环相似,我们还可定义双向链环。
三、综合例析
例10读入一串以"#"为结束标志的字符,统计每个字符出现的次数。
分析:
设置一个链表存放,每读入一个字符,就从链表的头结点向后扫描链表,如果在链表中此字符已存在,则其频率加1,否则将该字符的结点作为链表的新头结点,相应频率为1。
源程序如下:
programex11_10;
typeref=^letters;{定义指针ref:
含三个域key,count,next}
letters=record
key:
char;
count:
integer;
next:
ref;
end;
vark:
char;
sentinel,head:
ref;
proceduresearch(x:
char);{在链表中扫描查找x}
varw:
ref;
begin
w:
=head;
sentinel^.key:
=x;
whilew^.key<>xdow:
=w^.next;
ifw<>sentinel{若w<>sentinel,则说明x在链表中存在个数+1}
thenw^.count:
=w^.count+1
elsebegin{否则,在链表中的最后插入一个结点,存放该值}
w:
=head;new(head);
withhead^do
begin
key:
=x;count:
=1;next:
=w;
end
end;
end;{ofsearch}
procedureprintlist(w:
ref);{过程:
输出链表}
begin
whilew<>sentineldo
begin
writeln(w^.key:
2,w^.count:
10);
w:
=w^.next;
end;
end;{ofprintlist}
begin{mainprogram}
new(sentine);{建立一个空结点,这是第一个链表结点}
withsentinel^do
begin
key:
='#';count:
=0;next:
=nil;
end;
head:
=sentinel;{将head指向空结点(第一个结点),读入一个字符}
read(k);
whilek<>'#'do
begin
search(k);read(k);
end;
printlist(head);
end.
例11 用链表重写筛法求2~100之间所有素数程序。
源程序如下:
programex11_12;
usescrt;
typelink=^code;
code=record
key:
integer;
next:
link;
end;
varhead:
link;
procedureprintlist(h:
link);{打印链表h}
varp:
link;
begin
p:
=h;
whilep<>nildo
begin
write(p^.key,'-->');
p:
=p^.next;
end;
end;
procedurebuildlink;{建立链表}
varp,q:
link;
i:
integer;
begin
new(head);
head^.key:
=2;
p:
=head;
fori:
=3to100do
begin
new(q);
q^.key:
=i;
q^.next:
=nil;
p^.next:
=q;
p:
=q;
end;
end;
procedureprime;{筛法将找到的素数的倍数从链表中删除}
varh,p,q:
link;
begin
h:
=head;
whileh<>nildo
begin
p:
=h;q:
=p^.next;
whileq<>nildo
if(q^.keymodh^.key=0)then
begin
p^.next:
=q^.next;
dispose(q);
q:
=p^.next;
end
elsebegin
p:
=q;
q:
=q^.next;
end;
h:
=h^.next;
end;
end;
begin{mainprogram}
clrscr;
buildlink;
printlist(head);
writeln;
prime;
printlist(head);
end.
多项式(Polynomial)
n阶多项式Pn(x)有n+1项
–系数a0,a1,a2,…,an
–指数0,1,2,…,n。
按升幂排列
多项式的链接表示
在多项式的链表表示中每个结点增加了一个数据成员link,作为链接指针。
优点是:
多项式的项数可以动态地增长。
插入、删除方便,不移动元素。
–
多项式链表的相加
AH=1-10x6+2x8+7x14
BH=-x4+10x6-3x10+8x14+4x18
多项式的相加的算法
设立表头Aiter,Biter,检测指针pa,pb
•利用pa,pb扫描两个相加多项式,直到其中一个检测完毕:
–若当前被检测项指数相等,系数相加。
若不为0,则加入结果链表。
–若当前被检测项指数不等,将指数小者加入结果链表。
•若有一个多项式已检测完,将另一个多项式剩余部分加入结果链表。
程序:
Polynomial.pas
programpolynomial;
typedata=^term;
term=record
coef:
integer;
exp:
integer;
link:
data;
end;
var
first,pa,pb,ch1,ch2:
data;x,y:
integer;
procedureplink(head:
data);{建立多项式链表}
varp,h:
data;
x,y:
integer;
begin
p:
=head;read(x,y);
while
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- pascal 指针