表的指针实现

实现表的另一种方法是用指针将存储表元素的那些单元依次串联在一起。这种方法避免了在数组中用连续的单元存储元素的缺点,因而在执行插入或删除运算时,不再需要移动元素来腾出空间或填补空缺。然而我们为此付出的代价是,需要在每个单元中设置指针来表示表中元素之间的逻辑关系,因而增加了额外的存储空间的开销。

    为了将存储表元素的所有单元用指针串联起来,我们让每个单元包含一个元素域和一个指针域,其中的指针指向表中下一个元素所在的单元。例如,如果表是a1,a2,…,an ,那么含有元素ai的那个单元中的指针应指向含有元素ai+1的单元(i=1,2,…,n-1)。含有an的那个单元中的指针是空指针nil。此外,通常我们还为每一个表设置一个表头单元header,其中的指针指向开始元素中所在的单元,但表头单元header中不含任何元素。设置表头单元的目的是为了使表运算中的一些边界条件更容易处理。这一点我们在后面可以看到。如果我们愿意单独地处理诸如在表的第一个位置上进行插人与删除操作等边界情况,也可以简单地用一个指向表的第一个单元的指针来代替表头单元。

    上述这种用指针来表示表的结构通常称为单链接表,或简称为单链表链表。单链表的逻辑结构如图1所示。表示空表的单链表只有一个单元,即表头单元header,其中的指针是空指针nil。

图1 单链表示意图

    为了便于实现表的各种运算,在单链表中位置变量的意义与用数组实现的表不同。在单链表中位置i是一个指针,它所指向的单元是元素ai-1所在的单元,而不是元素ai所在的单元(i=2,3,…,n)。位置1是指向表头单元header的指针。位置End(L)是指向单链表L中最后一个单元的指针。这样做的目的是为了避免在修改单链表指针时需要找一个元素的前驱元素的麻烦,因为在单链表中只设置指向后继元素的指针,而没有设置指向前驱元素的指针。

    在单链表中,表类型TList和位置类型TPosition一样,都是指向某一单元的指针。尤其可以是指向表头单元的指针。

单链表结构的主要类型可形式地定义为:

type
CellType=record
           element:TElement;
           next:^CellType;
         end;
TList=^CellType;
TPosition=^CellType;

在单链表中,函数End(L)可实现如下

Function End(L:TList):TPosition;

var

      q: TPosition;

begin

   q:=L;

    while q^.next<>nil do q:=q^.next;

    return(q)

 end;

上述算法中的指针q指向需要检查的单元。由于检查要从header开始,而L是指向表头单元header的指针,所以q的初值为L。如果q所指的单元中的指针不是空指针,说明该单元不是表中的结束单元,因此要将q移向该单元的指针所指的下一单元,以便检查下一个单元。直到q所指的单元中的指针为nil时,那时的q值才是应返回的函数值。若表的长度为n,按这样计算End(L)需要扫描整个链表,因此需要O(n)时间,效率很低。如果需要频繁地调用函数End,我们可以采用下面的两种方法之一来提高效率。

  1. 在链表结构中多设一个指向链表结束单元的表尾指针。对此,通过表尾指针就可以在O(1)时间内实现End(L)。

  2. 尽可能避免调用End(L)。例如Purge程序的第(2)行中判断条件p<>End(L)应该用p.next<>nil来代替。

下面讨论单链表中Insert,Delete和Locate三种运算的实现。

函数 Insert(x,p)

功能

在表L的位置p处插入元素x,并将原来占据位置p的元素及其后面的元素都向后推移一个位置。例如,设L为a1,a2,…,an,那么在执行Insert(x,p)后,表L变为a1,a2,…ap-1,x,ap,…,an 。若p为End(L),那么表L变为a1,a2,…,an,x 。若表L中没有位置p,则该运算无定义。

实现

Procedure Insert(x:TElement;p:TPosition);

var

   temp:TPosition;

begin

   temp:=p^.next;

     new(p^.next);

   p^.next^.element:=x;

   p^.next^.next:=temp;

end;

说明

上述算法中,链表指针的修改情况见图2。

(a)

(b)

 图2 Insert过程的指针修改示意图

图2(a)是执行Insert运算之前的情况。我们要在指针p所指的单元之后插入一个新元素x。图2(b)是执行Insert运算以后的结果,其中的虚线表示新的指针。

在上述Insert算法中,位置变量p指向单链表中一个合法位置,要插入的新元素x应紧接在p所指单元的后面。指针p的合法性应在执行Insert运算之前判定。往一个单链表中插入新元素通常在表头或表尾进行,因此p的合法性容易判定。

复杂性

Insert运算所需的时间显然为O(1)。

函数 Delete(p)

功能

从表L中删除位置p处的元素。例如,当L为a1,a2,…,an时,执行Delete(p)后,L变为a1,a2,…,ap-1,ap+1,…,an 。当L中没有位置p或p=End(L)时,该运算无定义。

实现

Procedure Delete(p:TPosition);

var

  q:TPosition;

begin

   q:=p^.next;

      p^.next:=p^.next^.next;

   dispose(p);

end;

说明

这个过程很简单,其指针的修改如图3所示。

 图3  Delete过程的指针修改示意图

若要从一个表中删除一个元素x,但不知道它在表中的位置,则应先用Locate(x,L)找出指示要删除的元素的位置,然后再用Delete删除该位置指示的元素。

复杂性

Delete过程所需的时间显然也为O(1)。

函数 Locate(x,L)

功能

这是一个函数,函数值为元素x在L中的位置。若x在L中重复出现多次,则函数值为x第一次出现的位置。当x不在L中时,函数值为End(L)。

实现

Function  Locate(x:TElement;L:TList):TPosition;

Var

       p:TPosition;

begin

       p:=L;

       while p^.next<>nil do

         if p^.next^.element = x then return(p)

                                 else p:=p^.next;

       return(p);

end;

说明

在单链表中实现LOCATE的过程与用数组实现表时的LOCATE过程是类似的。

复杂性

容易看出,在最坏情况下和平均情况下,LOCATE所需的时间均为O(n)。

    对于其他基本的表运算实现起来都比较简单,这里从略。至于时间复杂性,在最坏情况下,PrintList显然需要O(n),而Previous由于单链表没有设置指向前驱的指针,得从头到尾扫描,因此也需要O(n)。

<script src="../../../lib/footer.js" type="text/javascript"> </script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值