数模论坛

 找回密码
 注-册-帐-号
搜索
热搜: 活动 交友 discuz
楼主: b

数据结构

[复制链接]
b
 楼主| 发表于 2004-5-29 04:10:53 | 显示全部楼层
<H2>表的性质</H2><>从表的定义不难看出表具有以下数学性质:除了表头和表尾外,表中的每一个元素有且仅有唯一的前驱和唯一的后继,表头有且只有一个后继,表尾有且只有一个前驱。</P><><B>注意:表和数组的区别</B></P><>从概念上来看,表是一种抽象数据类型;数组是一种具体的数据结构。关于<a href="http://algorithm.myrice.com/datastructure/ADT/chapter4.htm" target="_blank" >抽象数据类型和数据结构的区别</A>,请参见<a href="http://algorithm.myrice.com/datastructure/ADT/index.htm" target="_blank" >算法表达中的抽象机制</A>。</P><P>从数学性质上来看,表是一个二元关系R,&lt;x,y&gt;∈R 当且仅当x是y的前驱;如果将该二元关系用关系图(将每一个元素用一个点来表示,若x与y有关系则从x到y连一条有向线段)来表示,则得到的是一条单链a<SUB>1</SUB>→a<SUB>2</SUB>→…→a<SUB>n </SUB>,因此表也可以看成是特殊的图或特殊的树(每个节点有且仅有一个子节点);而数组是从1..n的自然数集合到数组元素集合的一一映射,其中n是数组的长度,1..n中每一个自然数唯一地对应着一个数组元素,反之亦然。所以数组可以用来实现映射。</P><P>从物理性质来看,数组中相邻的元素是连续地存储在内存中的,或者对于程序员来说可以认为它们是连续地存储在内存中的(比如动态增长的数组实际上并不存储在连续的内存中,但程序员可以认为它存储在连续的内存中),数组的存取对程序员来说是透明的;表只是一个抽象的数学结构,并不具有具体的物理形式,表需要通过其它有具体物理形式的数据结构来实现。在表的具体实现中,表中相邻的元素<b>不一定</b>存储在连续的内存空间中,除非表是用数组来实现的。对于数组,可以利用其下标在一个操作内随机存取任意位置上的元素;对于表,只能根据当前元素找到其前驱或后继,因此要存取第i个位置上的元素,一般不能在一个操作内实现,除非表是用数组实现的。在实现表时需要提供足够的信息以便能够通过表中元素的位置p来存取表中的元素。表中元素的位置p是指示表中元素位置的信息,通过对p进行处理(可能是从事某种函数运算计算出该元素在内存中的位置,也可能从表头开始,依次寻找当前元素的后继,重复i次找到第i个位置的元素)可以得到表中元素在内存中的物理位置,因此<b>不能简单地将位置p理解为类似数组下标的自然数</b>,实际上p可以是各种类型的变量,在数学上可将p定义为一个<DFN>偏序集</DFN>上的变量。在具体实现表及其运算时,应区分p与p所表示的位置,以及该位置上的元素三者之间的不同含义</P>
b
 楼主| 发表于 2004-5-29 04:11:17 | 显示全部楼层
<H2>ADT表的操作</H2><>表是一种非常简便的结构,我们可以根据需要改变表的长度,也可以在表中任何位置对元素进行访问、插人或删除等操作。我们还可以将两个表连接成一个表,或把一个表拆成几个表。表的结构在信息检索,程序设计语言的编译等许多方面有广泛的应用。</P><>在表的数学模型上,我们还要定义一组运算,才能使这一数学模型成为一个抽象数据类型表即ADT表。下面我们将给出一组典型的表运算。其中L是由类型为 TElement 的元素组成的一个表。x表示一个元素,p表示元素在表中的位置,其类型为TPosition。<B>在表的不同实现方式下,TPosition可能有不同的类型定义,作为位置变量的p也可能有不同的含义</B>。但是TPosition必须是一个偏序集,即任何两个TPosition类型的变量x,y之间可以进行比较运算,且结果只有x&gt;y,x&lt;y,x=y,x&gt;=y,x&lt;=y五种。</P><>下面我们将ADT表看作一个抽象类TList,L是该类的一个实体,ADT表的操作将定义成L的属性和方法。</P><P align=center><B>ADT表的基本运算</B></P><DIV align=center><CENTER><TABLE borderColor=#ffffff height=198 cellSpacing=0 cellPadding=5 width="90%" border=1><TR><TH align=middle width="25%" bgColor=#e0e0e0 height=16><FONT size=2>运算</FONT></TH><TH align=middle width="75%" bgColor=#e0e0e0 height=16><FONT size=2>含义</FONT></TH></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=15><FONT size=2>L.end</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=15><FONT size=2>为了方便,我们假定表L的结束元素a<SUB>n</SUB>之后还有一个位置,用属性end的值来表示这个位置。该属性的值为TPosition类型。</FONT></TD></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=15><FONT size=2>L.piquet</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=15><FONT size=2>为了方便,我们假定表L的开始元素a<SUB>1</SUB>之前还有一个位置,该为值称为哨兵位置,该位置上的元素称为哨兵元素,用属性Piquet的值来表示这个位置。该属性的值为TPosition类型。</FONT></TD></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=16><FONT size=2>L.first</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=16><FONT size=2>这是一个属性,其值为表L中第一个元素的位置。当L为空表时,值为L.end。</FONT></TD></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=15><FONT size=2>L.last</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=15><FONT size=2>这是一个属性,其值为表L中最后一个元素的位置。当L为空表时,值为L.end。</FONT></TD></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=15><FONT size=2>L.length</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=15><FONT size=2>返回表L的长度,即元素个数。</FONT></TD></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=15><FONT size=2>L.IsEmpty()</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=15><FONT size=2>如果表L为空表(长度为0)则返回true,否则返回false。</FONT></TD></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=16><FONT size=2>L.next(p)</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=16><FONT size=2>这是一个函数,函数值为表L中位置p的后继位置。如果p是L中结束元素的位置,则L.Next(p)=L.end。当L中没有位置p或p=L.end时,该运算无定义。</FONT></TD></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=16><FONT size=2>L.prev(p)</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=16><FONT size=2>这是一个函数,函数值为表L中位置p的前驱位置。当L中没有位置p或p是L中开始元素的位置时,该运算无定义。</FONT></TD></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=16><FONT size=2>L.get(p)</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=16><FONT size=2>这是一个函数,函数值为L中位置p处的元素。当p=L.end或L中没有位置p时,该运算无定义。</FONT></TD></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=15><FONT size=2>L.put(x,p)</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=15><FONT size=2>该方法将元素x放置到表L的位置p处,如果位置p处已有元素,则用x取代该元素;如果p=L.end或者L中没有位置p,则该运算无定义。</FONT></TD></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=16><FONT size=2>L.insert(x,p)</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=16><FONT size=2>在表L的位置p处插入元素x,并将原来占据位置p的元素及其后面的元素都向后推移一个位置。例如,设L为a<SUB>1</SUB>,a<SUB>2</SUB>,…,a<SUB>n</SUB>,那么在执行L.insert(x,p)后,表L变为a<SUB>1</SUB>,a<SUB>2</SUB>,…a<SUB>p-1</SUB>,x,a<SUB>p</SUB>,…,a<SUB>n </SUB>。若p为L.end,那么表L变为a<SUB>1</SUB>,a<SUB>2</SUB>,…,a<SUB>n</SUB>,x 。若表L中没有位置p,则该运算无定义。</FONT></TD></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=16><FONT size=2>L.delete(p)</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=16><FONT size=2>从表L中删除位置p处的元素。例如,当L为a<SUB>1</SUB>,a<SUB>2</SUB>,…,a<SUB>n</SUB>时,执行L.delete(p)后,L变为a<SUB>1</SUB>,a<SUB>2</SUB>,…,a<SUB>p-1</SUB>,a<SUB>p+1</SUB>,…,a<SUB>n</SUB> 。当L中没有位置p或p=L.end时,该运算无定义。</FONT></TD></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=16><FONT size=2>L.locate(x)</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=16><FONT size=2>这是一个函数,函数值为元素x在L中的位置。若x在L中重复出现多次,则函数值为x第一次出现的位置。当x不在L中时,函数值为L.end。</FONT></TD></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=16><FONT size=2>L.MakeEmpty</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=16><FONT size=2>这是一个将L变为空表的方法。</FONT></TD></TR><TR><TD align=middle width="25%" bgColor=#e0e0e0 height=16><FONT size=2>L.print</FONT></TD><TD width="75%" bgColor=#e0e0e0 height=16><FONT size=2>将表L中所有元素按位置的先后次序打印输出。</FONT></TD></TR></TABLE></CENTER></DIV><P>在表的数学模型上,定义了上述运算后,我们就定义了抽象数据类型(抽象类)TList。当然,并非任何时候都需要同时执行以上运算。在有些问题中只需要上述的一部分运算,因此也可以用上述运算的其中几个来定义适合特殊目的的抽象数据类型。上述表的运算是一些最基本的运算,对于实际问题中涉及的关于表的更复杂的运算,可以用这些基本运算的组合来实现。</P><P>例如,我们可以利用上述基本运算写一个过程Purge,来清除一个表L中重复出现的元素。其中 p,q为TPosition类型的变量。</P>
b
 楼主| 发表于 2004-5-29 04:12:14 | 显示全部楼层
<>================下面是Pascal语言描述的代码================</P><>
Procedure  Purge(var L:TList)
  var
    p,q:TPosition;
  begin
1   p:=L.first;
2   while p&lt;&gt;L.end do
     begin
3       q:=L.next(p);
4       while q&lt;&gt;L.end do
5           if  Equal(L.get(p),L.get(q)) then L.delete(q)
6                                        else q:=L.next(q);
7       p:=L.next(p);
     end
  end;</P><>================下面是C++语言描述的代码================</P><P>
void Purge(TList&amp; L)
{
    TPosition p,q;
1.  p=L.first();
2.  while (p!=L.end())
    {
3.     q=L.next(p);
4.     while (q!=L.end())
         {
5.         if (Equal(L.get(p),L.get(q))) L.delete(q);
6.                                  else q=L.next(q);  
         }
7.     p=L.next(p);      
    }
}</P><P>上述过程的参数是一个表L,其元素类型为TElement。过程中用到的函数Equal(x,y)的两个自变量均为TElement类型的元素。若x与y相同,则函数值为true,否则为false。这里所说的相同的含义要根据具体元素的类型来确定。例如,当TElement为实型时,当且仅当x=y时Equal(x,y)=true。但是如果TElement是一个有用户帐号、姓名及地址等多个域的记录,如:</P><P>================下面是Pascal语言描述的代码================</P><P>
type
TElement=record
            ID: integer;
            Name:  string[20];
            Address: string[50];
          end;</P><P>================下面是C++语言描述的代码================</P><P>
typedef struct TElement
{
  int ID;
  char Name[20];
  char Address[50];
};
这时我们可以规定,Equal(x,y)=true当且仅当x.ID=y.ID,即x与y的帐号相同时成立;也可以规定3个域的值都相同时Equal(x,y)=true。</P><P>在Purge过程中,变量p与q是表L的两个位置变量,第2-7行是关于变量p的循环。在循环中逐个检查位置p后面的每一个位置q,如果这两个位置上的元素相同,则将位置q的元素从表L中删去。当q遍历了p后面的所有位置之后,p变为下一个位置,再开始新的循环。在过程的第4-6行的内循环中,有一点值得注意:当执行到第5行删除了位置q的元素以后,表中原来在位置q+l,q+2,…的各元素都要向前移动一个位置。特别地,如果q恰好是结束元素的位置,那么在第5行删除了位置q的元素后,q值将变为L.end。因此内循环体第5和第6行的语句必须并行而不能串行,不然,可能会出现无定义的语句q=L.next(L.end)(即第6行的语句不用else而单独作为一个语句会出错)。在后文中,我们要给出TList和TPosition的适当类型说明,并讨论实现表运算的方法。</P><P>下面我们用抽象类来定义ADT表。</P><P>================下面是C++语言描述的代码================</P><P>
template &lt; class TElement &gt;
class TLiearList {
   public:
      virtual bool IsEmpty() const = 0;
      virtual int length() const = 0;
      virtual bool locate(int k, T&amp; x) const = 0;
         //return the k'th element of list in variable x
      virtual int Search(const T&amp; x) const = 0;
         //return position of x
      virtual AbstractList&amp; Delete(int k, T&amp; x) = 0;
         //delete k'th element of list and return in x
      virtual AbstractList&amp;
                      Insert(int k, const T&amp; x) = 0;
         //insert x just after k'th element
      virtual void Output(ostream&amp; out) const = 0;
};
</P>
b
 楼主| 发表于 2004-5-29 04:12:46 | 显示全部楼层
<H2>表的实现</H2><>下面讨论表的实现。表可以用许多数据结构来实现,最常用的是用数组和链表实现。</P><BLOCKQUOTE><UL><LI><a href="http://algorithm.myrice.com/datastructure/basic/list/chapter3_1.htm" target="_blank" >表的数组实现</A> <LI><a href="http://algorithm.myrice.com/datastructure/basic/list/chapter3_2.htm" target="_blank" >表的指针实现</A> <LI><a href="http://algorithm.myrice.com/datastructure/basic/list/chapter3_3.htm" target="_blank" >表的游标实现</A> <LI><a href="http://algorithm.myrice.com/datastructure/basic/list/chapter3_4.htm" target="_blank" >循环链表</A> <LI><a href="http://algorithm.myrice.com/datastructure/basic/list/chapter3_5.htm" target="_blank" >双链表</A> </LI></UL></BLOCKQUOTE><><B>说明</B></P><OL><LI>Error(Msg) —— 显示错误信息 Msg 并终止该子程序。 <LI>符号"←"表示赋值,y←x表示将x的值赋给y。使用这个符号表示赋值是因为某些变量具体类型需要根据实际情况来确定,因此不可以直接用赋值号":="进行赋值。 <LI>由于篇幅的关系,对于一些很容易实现的运算,这里就不再具体实现了。下面主要讨论Insert,Delete和Locate三种运算的实现</LI></OL><!-- #EndEditable -->
b
 楼主| 发表于 2004-5-29 04:13:20 | 显示全部楼层
<>表的数组实现
将一个表存储到计算机中,可以采用许多不同的方法,其中既简单又自然的是顺序存储方法,即将表中的元素逐个存放于数组的一些连续的存储单元中。在这种表示方式下,容易实现对表的遍历。要在表的尾部插入一个新元素,也很容易。但是要在表的中间位置插入一个新元素,就必须先将其后面的所有元素都后移一个单元,才能腾出新元素所需的位置。执行删除运算的情形类似。如果被删除的元素不是表中最后一个元素,则必须将它后面的所有元素前移一个位置,以填补由于删除所造成的空缺。</P><>用数组实现表时,我们将表类型TList定义为一个记录。它有两个域,第一个域是一个数组,用于存储表中的元素,数组的大小根据表可能达到的最大长度而定;第二个域是一个整型变量last,用于指出表中结束元素在数组中的位置。表中第i个元素(1≤i≤last)存储在数组的第i个单元中。</P><>在这种情况下,位置变量的类型TPosition是整型,位置变量p表示数组的第p个单元即表中第p个元素的位置。End(L)的函数值为last+1。这些类型可用Pascal语言说明如下:</P><P>Const
  MaxLength=100;  {数组的最大长度,即表的最大单元数目}
Type
  TPosition = Integer;
  TElement= ...  {TElement要根据具体情况而定}
  TList = Record
           Elements: Array [1..MaxLength] of TElement;
           last: TPosition;
          End;
定义了实现表的数组结构后,我们就可以讨论在这种结构上如何具体地实现表的基本运算了。</P><P>函数 Insert(x,p,L)
功能</P><P>在表L的位置p处插入元素x,并将原来占据位置p的元素及其后面的元素都向后推移一个位置。例如,设L为a1,a2,…,an,那么在执行Insert(x,p,L)后,表L变为a1,a2,…ap-1,x,ap,…,an 。若p为End(L),那么表L变为a1,a2,…,an,x 。若表L中没有位置p,则该运算无定义。</P><P>实现</P><P>Procedure Insert(x:TElement;p:TPosition;L:TList);</P><P>Var  q: TPosition;</P><P>begin</P><P> if Length(L)&gt;=MaxLength</P><P>   then</P><P>     Error('List is Full')</P><P>   else if (p&gt;End(L))or(p&lt;First(L))</P><P>    then</P><P>      Error('Position was not defined.')</P><P>    else</P><P>    begin</P><P>     for q←L.last downto p do</P><P>        L[q]←L[q+1];</P><P>              {将L的第q+1个元素复制到L的位置q上}</P><P>     L.last←L.last+1;</P><P>     L.Elements[p]← x;</P><P>    end;</P><P>end;</P><P>说明:</P><P>算法Insert将位于p,p+1,…,last 中的元素分别移到位置p+1,p+2,…,last+1 ,然后将新元素插入位置p。注意算法中元素后移的顺序,必须从表中最后一个位置开始后移,直至将位置p处的元素后移为止。如果新元素的插入位置不合法,则调用子程序Error输出错误信息,然后终止程序。</P><P>复杂性:</P><P>现在我们来分析算法的时间复杂性。这里问题的规模是表的长度Length(L)=L.last,设它的值为n。显然该算法的主要时间花费在for循环的元素后移上,该语句的执行次数为n-p+1。由此可看出,所需移动元素位置的次数不仅依赖于表的长度,而且还与插人的位置p有关。当p=n+1时由于循环变量的终值大于初值,元素后移语句将不执行,无须移动元素;若p=1,则元素后移语句将循环执行n次,需移动表中所有元素。也就是说该算法在最好情况下需要Ο(1)时间,在最坏情况下需要Ο(n)时间。由于插入可能在表中任何位置上进行,因此,有必要分析算法的平均性能。</P><P>设在长度为n的表中进行插入运算所需的元素移动次数的平均值为EIN(n)。由于在表中第i个位置上插入元素需要的移动次数为n-i+1,故</P><P>其中,pi表示在表中第i个位置上插入元素的概率。考虑最简单的情形即假设在表中任何合法位置i (1≤i≤n+l)上插入元素的机会是均等的,则:</P><P>从而,在等概率插入的情况下,</P><P>也就是说,用数组实现表时,在表中做插入运算,平均要移动表中一半的元素,因而算法所需的平均时间仍为Ο(n)。

函数 Delete(p,L)
功能</P><P>从表L中删除位置p处的元素。例如,当L为a1,a2,…,an时,执行Delete(p,L)后,L变为a1,a2,…,ap-1,ap+1,…,an 。当L中没有位置p或p=End(L)时,该运算无定义。</P><P>实现</P><P>Procedure Delete(p:TPosition;Var L:TList);</P><P>begin</P><P> if (p&gt;End(L)) or (p&lt;First(L))</P><P>   then Error('Position does not exist.')</P><P>   else</P><P>     begin</P><P>       L.last ← L.last-1;</P><P>       for q ← p to L.last do</P><P>       L.Elements[q] ← L.Elements[q+1];</P><P> end;</P><P>说明</P><P>算法Delete通过将位于p+1,p+2,…,last中的元素分别移到位置p,p+1,…, last-1来删除原来位置p处的元素。</P><P>复杂性</P><P>该算法的时间分析与插入算法类似,元素的移动次数也是由表长n和位置p决定的。若p=n,则由于循环变量的初值大于终值,前移语句将不执行,无须移动元素;若p=1,则前移语句将循环执行n-1次,需要移动表中除删除元素外的所有元素。因此,算法在最好情况下需要O(1)时间,而在最坏情况下需要O(n)时间。</P><P>删除运算的平均性能分析与插入运算类似。设在长度为n的表中删除一个元素所需的平均移动次数为EDE(N)。由于删除表中第i个位置上的元素需要的移动次数为n-i,故</P><P>其中,pi表示删除表中第i个位置上元素的概率。在等概率的假设下,</P><P>这时</P><P>也就是说用数组实现表时,在表中做删除运算,平均要移动表中约一半的元素,因而删除运算所需的平均时间为O(n)。

函数 Locate(x,L)
功能</P><P>这是一个函数,函数值为元素x在L中的位置。若x在L中重复出现多次,则函数值为x第一次出现的位置。当x不在L中时,函数值为End(L)。</P><P>实现</P><P>Function  Locate(x:TElement;var L:TLIST):TPosition;</P><P>var</P><P>     q:TPosition;</P><P>begin</P><P> for q:=1 to L.last do</P><P>          if L.elements[q]=x  then  return (q);</P><P>   return(L.last+1);</P><P>end;</P><P>说明</P><P>算法Locate在数组中从前向后通过比较来查找给定元素的位置。如果在表中没有找到这样的元素,则返回last+1。</P><P>复杂性</P><P>该算法中的基本运算是两个元素的比较。若在表中位置i找到给定元素,则需要进行i次比较,否则需要进行n次比较,n为表的长度。与算法Insert和Delete的时间分析类似,算法Locate在最好情况下需要O(l)时间,在最坏情况和平均情况下都需要O(n)时间。
</P><P>用数组来实现表时,我们利用了数组单元在物理位置上的邻接关系来表示表中元素之间的逻辑关系。由于这个原因,用数组来实现表有如下的优缺点。</P><P>优点是:</P><P>无须为表示表中元素之间的逻辑关系增加额外的存储空间;
可以方便地随机访问表中任一位置的元素。
缺点是:</P><P>插入和删除运算不方便,除表尾的位置外,在表的其他位置上进行插入或删除操作都必须移动大量元素,其效率较低;
由于数组要求占用连续的存储空间,存储分配只能预先进行静态分配。因此,当表长变化较大时,难以确定数组的合适的大小。确定大了将造成浪费
</P>
b
 楼主| 发表于 2004-5-29 04:13:58 | 显示全部楼层
<H3>表的指针实现</H3>
<>实现表的另一种方法是用指针将存储表元素的那些单元依次串联在一起。这种方法避免了在数组中用连续的单元存储元素的缺点,因而在执行插入或删除运算时,不再需要移动元素来腾出空间或填补空缺。然而我们为此付出的代价是,需要在每个单元中设置指针来表示表中元素之间的逻辑关系,因而增加了额外的存储空间的开销。</P>
<  align=left>    为了将存储表元素的所有单元用指针串联起来,我们让每个单元包含一个元素域和一个指针域,其中的指针指向表中下一个元素所在的单元。例如,如果表是a<SUB>1</SUB>,a<SUB>2</SUB>,…,a<SUB>n</SUB> ,那么含有元素a<SUB>i</SUB>的那个单元中的指针应指向含有元素a<SUB>i+1</SUB>的单元(i=1,2,…,n-1)。含有a<SUB>n</SUB>的那个单元中的指针是空指针nil。此外,通常我们还为每一个表设置一个表头单元header,其中的指针指向开始元素中所在的单元,但表头单元header中不含任何元素。设置表头单元的目的是为了使表运算中的一些边界条件更容易处理。这一点我们在后面可以看到。如果我们愿意单独地处理诸如在表的第一个位置上进行插人与删除操作等边界情况,也可以简单地用一个指向表的第一个单元的指针来代替表头单元。</P>
b
 楼主| 发表于 2004-5-29 04:15:15 | 显示全部楼层
  上述这种用指针来表示表的结构通常称为<B>单链接表</B>,或简称为<B>单链表</B>或<B>链表</B>。单链表的逻辑结构如图1所示。表示空表的单链表只有一个单元,即表头单元header,其中的指针是空指针nil。<BLOCKQUOTE>< align=center><img src="http://algorithm.myrice.com/datastructure/basic/list/images/img3.gif"></P></BLOCKQUOTE>< align=center>图1 单链表示意图</P>< left" align=left>    为了便于实现表的各种运算,在单链表中位置变量的意义与用数组实现的表不同。在单链表中位置i是一个指针,它所指向的单元是元素a<SUB>i-1</SUB>所在的单元,而不是元素a<SUB>i</SUB>所在的单元(i=2,3,…,n)。位置1是指向表头单元header的指针。位置End(L)是指向单链表L中最后一个单元的指针。这样做的目的是为了避免在修改单链表指针时需要找一个元素的前驱元素的麻烦,因为在单链表中只设置指向后继元素的指针,而没有设置指向前驱元素的指针。</P><P left" align=left>    在单链表中,表类型TList和位置类型TPosition一样,都是指向某一单元的指针。尤其可以是指向表头单元的指针。</P><P left" align=left>单链表结构的主要类型可形式地定义为:</P>
b
 楼主| 发表于 2004-5-29 04:15:45 | 显示全部楼层
<>type
CellType=record
           element:TElement;
           next:^CellType;
         end;
TList=^CellType;
TPosition=^CellType;
在单链表中,函数End(L)可实现如下</P><>Function End(L:TList):TPosition;</P><>var</P><P>      q: TPosition;</P><P>begin</P><P>   q:=L;</P><P>    while q^.next&lt;&gt;nil do q:=q^.next;</P><P>    return(q)</P><P> end;
</P><P>上述算法中的指针q指向需要检查的单元。由于检查要从header开始,而L是指向表头单元header的指针,所以q的初值为L。如果q所指的单元中的指针不是空指针,说明该单元不是表中的结束单元,因此要将q移向该单元的指针所指的下一单元,以便检查下一个单元。直到q所指的单元中的指针为nil时,那时的q值才是应返回的函数值。若表的长度为n,按这样计算End(L)需要扫描整个链表,因此需要O(n)时间,效率很低。如果需要频繁地调用函数End,我们可以采用下面的两种方法之一来提高效率。</P><P>在链表结构中多设一个指向链表结束单元的表尾指针。对此,通过表尾指针就可以在O(1)时间内实现End(L)。 </P><P>尽可能避免调用End(L)。例如Purge程序的第(2)行中判断条件p&lt;&gt;End(L)应该用p.next&lt;&gt;nil来代替。 </P><P>下面讨论单链表中Insert,Delete和Locate三种运算的实现。</P><P>函数 Insert(x,p)
功能</P><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,则该运算无定义。</P><P>实现</P><P>Procedure Insert(x:TElement;p:TPosition);</P><P>var</P><P>   temp:TPosition;</P><P>begin</P><P>   temp:=p^.next;</P><P>     new(p^.next);</P><P>   p^.next^.element:=x;</P><P>   p^.next^.next:=temp;</P><P>end;</P><P>
</P>
b
 楼主| 发表于 2004-5-29 04:16:16 | 显示全部楼层
<><B>说明</B></P><BLOCKQUOTE>< left" align=left>上述算法中,链表指针的修改情况见图2。</P></BLOCKQUOTE>< align=center><img src="http://algorithm.myrice.com/datastructure/basic/list/images/img4.gif"></P><P align=center>(a)</P><P align=center><img src="http://algorithm.myrice.com/datastructure/basic/list/images/img5.gif"></P><P align=center>(b)</P><BLOCKQUOTE><P align=center> 图2 Insert过程的指针修改示意图</P><P left" align=left>图2(a)是执行Insert运算之前的情况。我们要在指针p所指的单元之后插入一个新元素x。图2(b)是执行Insert运算以后的结果,其中的虚线表示新的指针。</P><P left" align=left>在上述Insert算法中,位置变量p指向单链表中一个合法位置,要插入的新元素x应紧接在p所指单元的后面。指针p的合法性应在执行Insert运算之前判定。往一个单链表中插入新元素通常在表头或表尾进行,因此p的合法性容易判定。</P></BLOCKQUOTE><P><B>复杂性</B></P><BLOCKQUOTE><P left" align=left>Insert运算所需的时间显然为<I>O</I>(1)。</P></BLOCKQUOTE>
b
 楼主| 发表于 2004-5-29 04:16:47 | 显示全部楼层
<TABLE 10pt" borderColor=#ffffff cellPadding=5 width="90%" border=1><TR><TD width="100%" bgColor=#e0e0e0><CENTER>< left" align=left><B>函数</B> Delete(p)</P>< left" align=left><B>功能</B></P><BLOCKQUOTE>< left" align=left><FONT size=2>从表L中删除位置p处的元素。例如,当L为a<SUB>1</SUB>,a<SUB>2</SUB>,…,a<SUB>n</SUB>时,执行Delete(p)后,L变为a<SUB>1</SUB>,a<SUB>2</SUB>,…,a<SUB>p-1</SUB>,a<SUB>p+1</SUB>,…,a<SUB>n</SUB> 。当L中没有位置p或p=End(L)时,该运算无定义。</FONT></P></BLOCKQUOTE><P left" align=left><B>实现</B></P><BLOCKQUOTE><P 5px; MARGIN-BOTTOM: 5px; TEXT-ALIGN: left" align=left><B>Procedure</B> Delete(p:TPosition);</P><P 5px; MARGIN-BOTTOM: 5px; TEXT-ALIGN: left" align=left>var</P><P 5px; MARGIN-BOTTOM: 5px; TEXT-ALIGN: left" align=left>  q:TPosition;</P><P 5px; MARGIN-BOTTOM: 5px; TEXT-ALIGN: left" align=left>begin</P><P 5px; MARGIN-BOTTOM: 5px; TEXT-ALIGN: left" align=left>   q:=p^.next;</P><P 5px; MARGIN-BOTTOM: 5px; TEXT-ALIGN: left" align=left>      p^.next:=p^.next^.next;</P><P 5px; MARGIN-BOTTOM: 5px; TEXT-ALIGN: left" align=left>   dispose(p);</P><P 5px; MARGIN-BOTTOM: 5px; TEXT-ALIGN: left" align=left>end;</P></BLOCKQUOTE><P left" align=left><B>说明</B></P><BLOCKQUOTE><P left" align=left>这个过程很简单,其指针的修改如图3所示。</P></BLOCKQUOTE><P align=center><img src="http://algorithm.myrice.com/datastructure/basic/list/images/img6.gif"></P></CENTER><P align=center> 图3  Delete过程的指针修改示意图</P><BLOCKQUOTE><CENTER><P left" align=left>若要从一个表中删除一个元素x,但不知道它在表中的位置,则应先用Locate(x,L)找出指示要删除的元素的位置,然后再用Delete删除该位置指示的元素。</P></CENTER></BLOCKQUOTE><CENTER><P left" align=left><B>复杂性</B></P><BLOCKQUOTE><P left" align=left>Delete过程所需的时间显然也为<I>O</I>(1)。</P></BLOCKQUOTE></CENTER></TD></TR><TR><TD width="100%" bgColor=#e0e0e0><B>函数</B> <FONT size=2>Locate(x,L)</FONT> <P><B>功能</B></P><BLOCKQUOTE><P><FONT size=2>这是一个函数,函数值为元素x在L中的位置。若x在L中重复出现多次,则函数值为x第一次出现的位置。当x不在L中时,函数值为End(L)。</FONT></P></BLOCKQUOTE><P><B>实现</B></P></TD></TR></TABLE>
您需要登录后才可以回帖 登录 | 注-册-帐-号

本版积分规则

小黑屋|手机版|Archiver|数学建模网 ( 湘ICP备11011602号 )

GMT+8, 2026-3-16 22:24 , Processed in 0.067345 second(s), 13 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表