树的父亲数组表示法

来源:互联网 发布:java手机qq通用版 编辑:程序博客网 时间:2024/05/06 15:26

设T是一棵树,表示T的一种最简单的方法是用一个一维数组存储每个结点,数组的下标就是结点的位置指针,每个结点中有一个指向各自的父亲结点的数组下标的域,这样可使Parent操作非常方便。类型定义如下:

Type

TPosition=integer; {结点的位置类型为整型}

 NodeType=Record

           Label:LabelType;    {该结点的标号}

           Parent:TPosition;   {该结点的父亲的数组下标,对于根结点该域为0}

          End;

 TreeType=Record

          NodeCount:integer;  {树的结点的总数目}

          Node:Array [1..MaxNodeCount] of NodeType;{存储树的结点}

          End;

由于树中每个结点的父亲是唯一的,所以上述的父亲数组表示法可以唯一地表示任何一棵树。在这种表示法下,寻找一个结点的父结点只需要O(1)时间。在树中可以从一个结点出发找出一条向上延伸到达其祖先的道路,即从一个结点到其父亲,再到其祖父等等。求这样的道路所需的时间正比于道路上结点的个数。在树的父亲数组表示法中,对于涉及查询儿子和兄弟信息的树操作,可能要遍历整个数组。为了节省查询时间,可以规定指示儿子的数组下标值大于父亲的数组下标值,而指示兄弟结点的数组下标值随着兄弟的从左到右是递增的。

父亲数组实现的ADT树操作

函数 Parent(v,T)

功能

这是一个求父结点的函数,函数值为树T中标号为v的结点的父亲。当v是根结点时,函数值为0,表示结点v没有父结点。

实现

Function Parent(v:TPosition;var T:TreeType):TPosition;

begin

 return(T.Node[v].Parent);

end;

说明

由于每个结点都有一个域存储了其父亲结点的标号(数组下标),因此Parent操作实现非常简单。

复杂性

显然为O(1)。

函数 Leftmost_Child(v,T)

功能

这是一个求最左儿子结点的函数。函数值为树T中标号为v的结点的最左儿子。当v是叶结点时,函数值为0,表示结点v没有儿子。

实现

Function Leftmost_Child(v:TPosition;var  T:TreeType):TPosition;

begin

 i:=v+1;

 while (i<=T.NodeCount)and(T.Node[i].Parent<>v) do  inc(i);

 if i=T.NodeCount+1 then return(0)

                                             else return(i);

end;

说明

因为没有保存每个结点的子结点的信息,因此只能依次扫描每个结点,根据我们的约定,子结点一定排在父结点的后面,且兄弟结点的下标从左到右依次递增,因此第一次遇到的父亲是n的结点就是n的最左结点。

复杂性

该算法的复杂性取决于while循环。若设T.NodeCount=n,显然,在最坏情况下循环执行n-v次,最好情况下执行1次,平均情况下执行(n-v)/2,所以无论何种情况下,复杂性都为O(n)。

函数 Right_Sibling(v,T)

功能

这是一个求右邻兄弟的函数,函数值为树T中结点v的右邻兄弟。当v没有右邻兄弟时,函数值为0。

实现

Function Right_Sibling(v:TPosition;var  T:TreeType):TPosition;

begin

 i:=v+1;

 while (i<=T.NodeCount)and(T.Node[i].Parent<>T.Node[v].Parent) do

     inc(i);

 if i=T.NodeCount+1 then return(0)

                                             else return(i);

end;

说明

依次搜索排在v之后的结点,遇到第一个与v有相同父结点的结点就是v的右邻兄弟。

复杂性

同Leftmost_Child一样,该函数复杂性为O(n),其中n为树的总结点数。

函数 Create(i,x,T1,T2,..,Ti)

功能

这是一族建树过程。对于每一个非负整数i,该函数生成一棵新树T,T的根结点是标号为x的新结点v,并令v有i个儿子,这些儿子从左到右分别为树T1,T2,..,Ti的根。当i=0时,v既是树根,又是树叶。

实现

Procedure Create(i:integer;var x:LabelType;var T1,T2,..,Ti,T:TreeType);

var

k,j,father:integer;

begin

 with T do

 begin

 NodeCount:=1;

 Node[1].Label:=x;

 Node[1].Parent:=0;  {生成根结点}

 for k:=1 to i do

  if Tk.NodeCount<>0 then

  begin

   inc(NodeCount);

   Node[NodeCount]:=Tk.Node[1];

   Node[NodeCount].Parent:=1;{修改Tk的根结点的父亲使其指向T的根}

   father:=NodeCount;  {记下Tk的根结点的标号}

   for j:=2 to Tk.NodeCount do

    beign

    inc(NodeCount);

    Node[NodeCount]:=Tk.Node[j];

    Node[NodeCount].Parent:=Node[NodeCount].Parent+father-1;

      {修改Tk的每一个非根结点的父亲,因为Tk的根结点的位置改变了}

    end;    

  end;

  end;

end;

说明

这个过程首先生成一个新结点,其中存储的数据为x,新结点在数组T的第一个元素位置上;然后对于每一个Tk,1≤k≤i,如果Tk不为空则将Tk的每一个结点复制到T中,同时修改Tk的每一个元素的父结点,因为Tk的根结点在T中的下标已经不是1了,而是father,因此Tk的每一个元素的父结点的下标都应给增加一个增量father-1。

复杂性

如果∑(Tk的结点数)=n,即生成的新树的结点总数为n,则复杂性为O(n)。

Label,Root和MakeNull比较简单,这里就不一一实现了。

我们可以看出,用父亲数组实现树,比较容易实现Parent运算,但是对于Leftmost_Child和Right_Sibling则效率不高,而且这种实现方法很占用内存。但实现上比较简单。

原创粉丝点击