二叉树的顺序存储结构

此结构是将二叉树的所有结点,按照一定的次序,存储到一片连续的存储单元中。因此,必须将结点排成一个适当的线性序列,使得结点在这个序列中的相应位置能反映出结点之间的逻辑关系。这种结构特别适用于近似满二叉树

在一棵具有n个结点的近似满二叉树中,我们从树根起,自上层到下层,逐层从左到右给所有结点编号,就能得到一个足以反映整个二叉树结构的线性序列,如图6所示。其中每个结点的编号就作为结点的名称。

图6 近似满二叉树的结点编号

因此,我们可以对树的类型作如下说明:

TPosition=integer;

TreeType=record

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

           NodeList:array [1..MaxNodeCount] of LabelType; {存储结点的数组}

         end;

将数组下标作为结点名称(编号),就可将二叉树中所有结点的标号存储在一维数组中。例如,图6中的二叉树的顺序存储结构如图7所示。

图7 近似满二叉树的顺序存储结构


    在二叉树的这种表示方式下,各结点之间的逻辑关系是隐含表示的。近似满二叉树中,除最下面一层外,各层都充满了结点。可能除最底层外,每一层的结点个数恰好是上一层结点个数的2倍。因此,从一个结点的编号就可推知其父亲,左、右儿子,和兄弟等结点的编号。例如,对于结点i我们有:

  1. 仅当i=1时,结点i为根结点;

  2. 当i>1时,结点i的父结点为i/2

  3. 结点i的左儿子结点为2i;

  4. 结点i的右儿子结点为2i+1;

  5. 当i为奇数且不为1时,结点i的左兄弟结点为i-1;

  6. 当i为偶数时,结点i的右兄弟结点为i+1。

由上述关系可知,近似满二叉树中结点的层次关系足以反映结点之间的逻辑关系。因此,对近似满二叉树而言,顺序存储结构既简单又节省存储空间。

对于一般的二叉树,采用顺序存储时,为了能用结点在数组中的位置来表示结点之间的逻辑关系,也必须按近似满二叉树的形式来存储树中的结点。显然,这将造成存储空间的浪费。在最坏情况下,一个只有k个结点的右单枝树却需要2k-1个结点的存储空间。例如,只有3个结点的右单枝树,如图8(a)所示,添上一些实际不存在的虚结点后,成为一棵近似满二叉树,相应的顺序存储结构如图8(b)所示。

图8 一般二叉树的顺序存储结构

下面我们就用这种顺序存储结构来实现二叉树的常用操作。在这种表示法中,整数0表示空结点∧。对于非近似满二叉树,我们将其补为近似满二叉树,并规定一个特殊的标号&,用来表示补充的结点,&要根据标号的具体类型来确定。

顺序存储结构实现ADT二叉树的操作

函数 Parent(v,T);

功能

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

实现

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

begin

 return(v div 2);

end;

说明

根据这种表示法,我们知道,当i>1时,结点i的父结点为i/2

复杂性

显然为O(1)。

函数 Left_Child(v,T);

功能

这是一个求左儿子结点的函数。函数值为树T中结点v的左儿子。当结点v没有左儿子时,函数值为∧。

实现

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

begin

 if (2*v>T.NodeCount)or(T.NodeList[2*v]=&) then return(0)

                                            else return(2*v);

end;

说明

如果结点v的左儿子存在,则其下标为2*v。

复杂性

显然为O(1)。

函数 Right_Child(v,T);

功能

这是一个求右儿子结点的函数。函数值为树T中结点v的右儿子。当结点v没有右儿子时,函数值为∧。

实现

Procedure Right_Child(v:TPosition;var T:TreeType):TPosition;

begin

 if (2*v+1>T.NodeCount)or(T.NodeList[2*v+1]=&) then return(0)

                                                else return(2*v+1);

end;

说明

如果结点v的左儿子存在,则其下标为2*v+1。

复杂性

显然为O(1)。

函数 Create(x,Left,Right,T);

功能

这是一个建树过程。该函数生成一棵新的二叉树T,T的根结点标号为x,左右儿子分别为Left和Right。

实现

Procedure Create(x:LabelType;var Left,Right,T:TreeType);

begin

 T.NodeList[1]:=x;

 T.NodeCount:=Left.NodeCount+Right.NodeCount+1;

 h_left:=cal(Left.NodeCount);

 h_right:=cal(Right.NodeCount);{分别计算Left和Right的高度}

 if h_left>h_Right then h:=h_left else h:=h_right;{新树T的高度为h+1}

 for i:=Left.NodeCount+1 to (1 shl (h+1))-1 do Left.NodeList[i]:=&;

 Left.NodeCount:=(1 shl (h+1))-1;

  {将Left补成高度为h的满二叉树;其中shl是左移位运算,用来计算2的幂}

if h_right<h then

   begin

    for i:=Right.NodeCount+1 to (1 shl h)-1 do Right.NodeList[i]:=&;

    Right.NodeCount:=(1 shl h)-1;

   end;

  {若Right的高度小于h,则将Right补成高度为h-1的满二叉树}

 

{=======

对于Left中编号为i的结点v,它在新树T中的编号为2h +i,其中h=log2(i+1)-1 ;对于Right中编号为i的结点v,它在新树T中的编号为2h+1+i,其中h=log2(i+1)-1

=======}

 

 for i:=1 to Left.NodeCount do

   T.NodeList[(1 shl cal(i))+i]:=Left.NodeList[i];

{计算出Left中编号为i的结点在T中的位置,将其复制到T中}

 for i:=1 to Right.NodeCount do

   T.NodeList[(1 shl (cal(i)+1))+i]:=Right.NodeList[i];

{计算出Right中编号为i的结点在T中的位置,将其复制到T中}

end;

 

其中cal(i)用来计算含有i个结点的近似满二叉树T的高度,cal(i)=log2(i+1)-1,可以实现如下:

 

Function cal(i:integer;):integer;

var

x:real;

begin

 x:=log2(i+1)-1;

 if x=int(x) then return(x) else return(int(x)+1); {向上取整}

end;

 

其中log2(n)计算实数n以2为底的对数。

说明

 

在顺序存储的结构下,建立一棵新的二叉树的过程比较复杂。我们首先给出以下几个命题:

 

命题一

一棵高度为h的满二叉树有2h+1-1个结点。

证明:

满二叉树的第i层有2i个结点,i=0,1,2,...,h(树根为第0层),因此高度为h的满二叉树有20+21+..+2h = 2h+1-1个结点。

推论一

我们从树根起,自上层到下层,逐层从左到右给二叉树的所有结点编号,如图6所示,则近似满二叉树的第h层的从左到右第k个结点的编号为2h+k-1。

证明:

由于是近似满二叉树,所以第0层到第h-1层是满二叉树,根据命题一知道共有2h-1个结点,因此第h层的从左到右第1个结点的编号为2h-1+1,第h层的从左到右第k个结点的编号为2h-1+k。

推论二

一棵有n个结点的近似满二叉树,高度为log2(n+1)-1 ,其中 是天花板符号, x表示大于等于x的最小整数。

证明:

有n个结点的近似满二叉树,若其高度为h,则满足2h-1<n≤2h+1-1,化简得 log2(n+1)-1 ≤ h < [log2(n+1)-1]+1,即h=log2(n+1)-1

推论三

在近似满二叉树T中,设编号为i的结点处于T的第h层从左到右第k个位置上,则h=log2(i+1)-1 ,k=i-(2h-1)。

证明:

我们先不考虑编号大于i的结点,则前i个结点构成一棵近似满二叉树,根据推论二知其层数为h=log2(i+1)-1 ,又因为第0层到第h-1层是满二叉树,根据命题一知道共有2h-1个结点,所以编号为i的结点处于第h层的第k=i-(2h-1)个位置上。

我们用T(h,k)表示树T的第h层的第k个结点,则有下列命题:

 

命题二

Left(h,k)=T(h+1,k),Right(h,k)=T(h+1,k+2h),其中Left和Right分别是树T的根结点的左右子树。

证明:

显然。

我们用N(v,T)表示结点v在生成的新树T中的编号,则有下列命题:

 

命题三

对于Left中编号为i的结点v,N(v,T)=2h +i,其中h=log2(i+1)-1 ;对于Right中编号为i的结点v,N(v,T)=2h+1+i,其中h=log2(i+1)-1

证明:

在Left中编号为i的结点,根据推论三,他处于Left的第h=log2(i+1)-1 层,第k=i-(2h-1)个位置上。根据命题二该结点处于新树T的第h+1层第k个位置上,所以根据推论一,它在二叉树T中的编号为2h+1+k-1=2*2h+[i-(2h-1)]-1=2h+i。结点在Right中的情况同理可证。

 

有了命题三,我们就可以完成建树的过程。算法如下:

  1. 根据推论二计算Left和Right的高度,分别为hLeft和hRight

  2. 设h=max{hLeft,hRight},新树T的高度就为h+1;

  3. 将Left补成高度为h的满二叉树;

  4. 若hRight<h,则将Right补成高度为h-1的满二叉树;

  5. 依次扫描Left的每一个结点,根据命题三计算出Left中编号为i的结点在T中的位置,将其复制到T中;

  6. 依次扫描Right的每一个结点,根据命题三计算出Right中编号为i的结点在T中的位置,将其复制到T中;

具体程序见前文的实现。

复杂性

算法的主要时间花在扫描和赋值结点上,设新树有n个结点,则复杂性为O(n)。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1. 一棵二叉树的顺序存储情况如下: 树中,度为2的结点数为( )。 A.1 B.2 C.3 D.4 2. 一棵“完全二叉树”结点数为25,高度为( )。 A.4 B.5 C.6 D.不确定 3.下列说法中,( )是正确的。 A. 二叉树就是度为2的树 B. 二叉树中不存在度大于2的结点 C. 二叉树是有序树 D. 二叉树中每个结点的度均为2 4.一棵二叉树的前序遍历序列为ABCDEFG,它的中序遍历序列可能是( )。 A. CABDEFG B. BCDAEFG C. DACEFBG D. ADBCFEG 5.线索二叉树中的线索指的是( )。 A.左孩子 B.遍历 C.指针 D.标志 6. 建立线索二叉树的目的是( )。 A. 方便查找某结点的前驱或后继 B. 方便二叉树的插入与删除 C. 方便查找某结点的双亲 D. 使二叉树的遍历结果唯一 7. 有abc三个结点的右单枝二叉树顺序存储结构应该用( )示意。 A. a b c B. a b ^ c C. a b ^ ^ c D. a ^ b ^ ^ ^ c 8. 一颗有2046个结点的完全二叉树的第10层上共有( )个结点。 A. 511 B. 512 C. 1023 D. 1024 9. 一棵完全二叉树一定是一棵( )。 A. 平衡二叉树 B. 二叉排序树 C. 堆 D. 哈夫曼树 10.某二叉树的中序遍历序列和后序遍历序列正好相反,则该二叉树一定是( )的二叉树。 A.空或只有一个结点 B.高度等于其结点数 C.任一结点无左孩子 D.任一结点无右孩子 11.一棵二叉树的顺序存储情况如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 A B C D E 0 F 0 0 G H 0 0 0 X 结点D的左孩子结点为( )。 A.E B.C C.F D.没有 12.一棵“完全二叉树”结点数为25,高度为( )。 A.4 B.5 C.6 D.不确定 二、填空题(每空3分,共18分)。 1. 树的路径长度:是从树根到每个结点的路径长度之和。对结点数相同的树来说,路径长度最短的是 完全 二叉树。 2. 在有n个叶子结点的哈夫曼树中,总结点数是 2n-1 。 3. 在有n个结点的二叉链表中,值为非空的链域的个数为 n-1 。 4. 某二叉树的中序遍历序列和后序遍历序列正好相反,则该二叉树一定是 任一结点无左孩子 的二叉树。 5. 深度为 k 的二叉树最多有 个结点,最少有 k 个结点。 三、综合题(共58分)。 1. 假定字符集{a,b,c,d,e,f }中的字符在电码中出现的次数如下: 字符 a b c d e f 频度 9 12 20 23 15 5 构造一棵哈夫曼树(6分),给出每个字符的哈夫曼编码(4分),并计算哈夫曼树的加权路径长度WPL(2分)。 (符合WPL最小的均为哈夫曼树,答案不唯一) 哈夫曼编码: 2. 假设用于通信的电文由字符集{a,b,c,d,e,f,g}中的字符构成,它们在电文中出现的频率分别为{0.31,0.16,0.10,0.08,0.11,0.20,0.04}。要求: (1)为这7个字符设计哈夫曼树(6分)。 (2)据此哈夫曼树设计哈夫曼编码(4分)。 (3)假设电文的长度为100字符,使用哈夫曼编码比使用3位二进制数等长编码使电文总长压缩多少?(4分) (1) 为这7个字符设计哈夫曼树为(符合WPL最小的均为哈夫曼树,答案不唯一): (2) 哈夫曼编码为: a:01;b:001;c:100;d:0001;e:101;f:11;g:0000 (3) 假设电文的长度为100字符,使用哈夫曼编码比使用3位二进制数等长编码使电文总长压缩多少? 采用等长码,100个字符需要300位二进制数,采用哈夫曼编码发送这100个字符需要261二进制位,压缩了300-261=39个字符。压缩比为39/300=13%。 3. 二叉数T的(双亲到孩子的)边集为: { <A,B>, <A,C>, <D,A>, <D,E>, <E,F>, <F,G> } 请回答下列问题: (1)T的根结点(2分): (2)T的叶结点(2分): (3)T的深度(2分): (4)如果上述列出边集中,某个结点只有一个孩子时,均为其左孩子;某个结点有两个孩子时,则先列出了连接左孩子的边后列出了连接右孩子的边。画出二叉树其及前序线索(6分)。 (1)T的根结点 (2)T的叶结点 : (3)T的深度 : (4)该二叉树其及前序线索为: 4.现有以下按前序和中序遍历二叉树的结果: 前序:ABCEDFGHI 中序:CEBGFHDAI 画出二叉树的逻辑结构图(5分),并在图中加入中序线索(5分)。 5.有电文:ABCDBCDCBDDBACBCCFCDBBBEBB。 用Huffman树构造电文中每一字符的最优通讯编码。画出构造的哈夫曼树,并给出每个字符的哈夫曼编码方案。(符合WPL最小的均为哈夫曼树,答案不唯一) (1)构造哈夫曼树(6分): (2)哈夫曼编码方案(4分):

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值