链表2
一、链表
链表是这样的一种线性表,它的元素由数据和指针两部分组成,数据部分存放结点的有关信息,指针部分存放下一个结点的位置。
二、单链表及其基本操作
在链式存储结构的线性表中,数据元素的存储空间一般是不连续的。链式存储结构懂得线性表由若干个结点组成,每个结点组成,每个结点有两个域:一个是数据域;另一个是指针域。单链表的定义如下:
type
pointer=^nodetype;
nodetype=record
data:datatype;
next:pointer;
end;
var head:pointer;
head为表的首指针,指向链表的第一个结点。有时在链表的第一个结点前附设一个结点,该结点称为“哨兵”,head指向“哨兵”。整个链表的存取必须从head指针出发,沿着每个结点的next指针顺序进行,最后一个结点的next指针为“空”(nil)。如下图所示:
存储地址 数据域 指针域
1 li 29
5 sun 9
9 qian 1
13 wu nil
17 wang 25
21 zhou 5
25 zhao 13
29 zheng 17
在单链表中每个结点的存储地址是可以任意分配,即表中元素的存储位置可以不连续且是无序的,而它们的逻辑顺序由链表中的next指针确定;假设线性表a1,a2,…,an采用单链表的存储结构,其中p指针指向ai的结点,则p^.next指针指向值为ai+1的结点。也就是说,p^.data=ai,p^.next^.data=ai+1
单链表操作
(1)动态建立单链表
设线性表a1,a2,…,an。试建立其单链表存储结构,其中head指向单链表的“哨兵”。
procedure creatlink(var head:pointer);
begin
head:=nil;
for i:=n downto 1 do
begin
new(p);
p^.data=a[i];
p^.next:=head;
head:=p;
end;
new(p);
p^.next:=head;
head:=p;
end;
(2)插入操作
在单链表的第i个结点前插入元素x0。head为指向哨兵的首指针。
procedure insertlink(x:datatype;i:integer;var head:pointer);
begin
new(s);
s^.data:=x;
p:=head;j:=0;
while (p<>nil) and (j<i-1) do
begin
p:=p^.next;
j:=j+1;
end;{while}
if p<>nil
then begin
s^.next:=p^.next;
p^.next:=s;
end{then}
else writeln(‘without’);
end;
(3)删除操作
设head为指向“哨兵”的首指针。删除单链表的第i个结点。
procedure delelink(i:integer;var head:pointer);
begin
p:=head;j:=0;
while (p^.next<>nil) and (j<i-1) do
begin
p:=p^.next;
j:=j+1;
end;{while}
if p^.next=nil
then writeln(‘without’)
else begin
q:=p^.next;
p^.next:=p^.next^.next;
dispose(q);
end;{else}
end;
(4)读取单链表元素
设head为指向“哨兵”的首指针。读取单链表中的第i个数据元素。
function getlink(i:integer;var head:pointer):datatype;
begin
p:=head^.next;j:=1;
while (p<>nil) and (j<i) do
begin
p:=p^.next;
j:=j+1;
end;{while}
if p=nil
then writeln(‘without’)
else geglink:=p^.data;
end;
二、双向循环链表及其基本操作
以上讨论的单链表的结点中,只有一个指示后继结点的指针域next。因此,从某个结点出发,只能顺着next指针往后寻找其他结点。若要寻找某结点的直接前趋,则需从“哨兵”head出发。另外,对于空表与第一个结点的处理必须单独考虑,使得空表与非空表的运行不统一。为了克服这些缺点,引入了双向循环链表。
所谓双向循环链表,就是链表的结点中有两个指针域,一个指向直接后继(next),另一个指向直接前趋(priou);并且在双向循环链表中,“哨兵”的data为任意或者根据需要设置,next域指向线性表的第一个结点,priou域指向线性表的最后一个结点,哨兵指针指向“哨兵”;双向循环链表中最后一个结点的next域指向“哨兵”,即在双向循环链表中,所有结点的指针构造了一个环状链。如下图所示:
双向循环链表的基本操作:
(1)构造双向循环链表L
假设线性表中有n个数据元素a1,a2,…,an。试建立一个双向循环链表存储结构,L为双向循环链表的哨兵:
procedure creat_list(var L:dupointer);
begin
new(L);L^.next:=L;L^.priou:=L;q:=L;
for i:=1 to n do
begin
new(p);p^.data:=a[i];
q^.next:=p;
p^.priou:=q;q:=p;
end;{for}
q^.next:=L;L^.priou:=q;
end;
(2)读取双向循环链表中第i个结点的地址
在L为 “哨兵”的双向循环链表中,确定第i个结点的地址。若i>表长,返回nil;若i=表长+1,则返回哨兵指针L;
function geg_list(i,L):dupoiniter;
begin
p:=L^.next;j:=1;
while (p<>L) and (j<i) do
begin
p:=p^.next;j:=j+1;
end;{while}
if j=i then get_list:=p
else get_list:=nil;
end;
(3)插入操作
在双向循环链表L的位置p处插入一个新元素x的过程insert可实现如下 :
procedure insert(x:datatype;p:dupointer;var L:tlist);
var
q:dupointer;
begin
new(q);
q^.data:=x;
q^.priou:=p^.priou;
q^.next:=p;
p^.priou^.next:=q;
p^.priou:=q;
end;
上述算法对链表指针的修改情况如下图所示:
算法insert中没有检查位置p的合法性,因此在调用insert之前应保证位置p的合法性。 由于插入通常是在表的头尾两 端进行的,所以容易检查位置p的合法性。
(4)删除操作
在双向循环链表中,删除位置p处的元素操作可实现如下:
procedure delete(p:dupointer;var L:tlist);
begin
if (p<>nil) and (p<>L) then
begin
p^.priou^.next:=p^.next;
p^.next^.priou:=p^.priou;
dispose(p);
end;
end;
上述算法对链表指针的修改情况如下图所示:
与单链表中的删除算法类似,上述算法是在已知要删除元素在链表中的位置p时,将该位置所指的单元删去。若要从一个表中删除一个元素x但不知道它在表中的位置,则应先用定位函数Locate(x,L)找出要删除元素的位置,然后再用delete删除之。
(5)定位函数
在双向循环链表中,定位函数Locate可实现如下:
function Locate(x:datatype;L:tlist):dupointer;
var
p:dupointer;
begin
p:=L^.next;
while p<>L do
if p^.data=x then return(p)
else p:=p^.next;
return(nil);
end;