【笔记】树的表示与实现

1.树的抽象数据型

  与二叉树一样,在树上规定如下基本操作,可以把树定义成抽象数据型。

  • Parent(n,T):返回树T结点n的父亲。若n是根,则返回空。
  • LeftMostChild(n,T):返回树T结点的最左儿子。
  • RightSibling(n,T):返回树T结点n的右兄弟。
  • Data(n,T):返回树T结点n的DATA域的值。
  • Create( v,T1,T2,,Tk ):建立DATA域为v的根结点r,此结点有k颗子树 T1,T2,,Tk ,且 T1,T2,,Tk 是自左而右排列的。返回以r为根的树;若k=0,则r既是根也是叶结点。
  • Root(T):返回树T的根结点。

  通常可以对树定义3种遍历顺序,假设各子树记为 T1,T2,,Tk 。则树的遍历定义如下:

  (1)先根顺序

  • 访问根结点。
  • 先根顺序遍历 T1
  • 先根顺序遍历 T2
  • 先根顺序遍历 Tk
  • 退出

  (2)中根顺序

  • 中根顺序遍历 T1
  • 访问根结点。
  • 中根顺序遍历 T2
  • 中根顺序遍历 Tk
  • 退出

  (3)后根顺序

  • 后根顺序遍历 T1
  • 后根顺序遍历 T2
  • 后根顺序遍历 Tk
  • 访问根结点。
  • 退出

  树的先根遍历算法:

void PreOrder(node n,TREE T)
/*按先根顺序列出树T中结点n及其所有后代的数据域的值*/
{
    node c;
    visit(Data(n,T));
    c=LeftMostChild(n,T);
    while(c!=NULL)
    {
        PreOrder(c,T);
        c=RightSibling(c,T);
    }
}

  如果要按先跟顺序遍历整棵树T,则只要调用PreOrder(Root(T),T)即可。

2.树的表示

树的数组表示

  设T是一棵树,其中的结点命名为1,2,3,…,n。根据树中每个结点只有一个父亲这个特性,可用数组A来表示树T。令数组的下标对应于结点名,数组元素A[i]定义如下:

A[i]={j,0,iji

  这种表示法除根结点外,每个结点只有一个指向其父亲结点的链域。所以有时把这种表示法称为树的父链表示法或单链表示法。


这里写图片描述

  一般来说,Parent(i)=A[i]。数组元素由parent和data两个域构成。可以把上图表示的树的结构描述为:

struct node
{
    int parent;
    char data;
};
typedef node TREE[11];
TREE T;


这里写图片描述

  这种只有父链的表示方法,对于求父结点及祖先结点都很方便。但对于给定的结点n,欲求其子结点的信息相当困难,也不能反映各兄弟结点的顺序。但是如果在给结点命名时,可按照这样一种顺序:对每个结点n,在对n进行编号之后,按自左而右递增的顺序依次对n的儿子进行编号。在此假设之下,将很容易实现RightSibling和LeftMostChild。

typedef int node;
typedef node TREE[maxnodes];
node LeftMostChild(node n,TREE T)
{
    node i;
    for(i=n+1;i<=maxnodes-1;i++)
        if(T[i]==n)
            return i;
    return 0;
}

树的邻接表表示

  树的一种重要表示法是每个结点的所有子结点形成一个线性表,但因每个结点所具有的子结点个数不同,所以通常都采用链接式表示。
  上图中的树为例可以表示为下图所示。其中有一个数组header,每一个数组元素header[i]是一个表头结点,指向结点i的儿子结点链表的首结点。对于每个结点i,由header[i]出发,可以很容易地将其全部儿子结点找到。


这里写图片描述

  邻接表的类型说明:

typedef int node;
struct celltype
{
    node element;
    celltype *next;
};
typedef celltype *LIST;
typedef celltype *position;
struct TREE
{
    LIST header[maxnodes];
    datatype data[maxnodes];
    node root;
};

  用树的这种表示法实现LeftMostChild操作:

node LeftMostChild(node n ,TREE T)
{
    LIST L;
    L=T.header[n];
    if(Empty(L))
        return 0;
    else
        return(Retrieve(First(L),L));
}

树的左右链表示法

  通常,可以按照其自然形式,令每个结点除其信息域外,设置多个链域,使它们指向相应的儿子结点,但这样会使每个结点占用的空间较多。为了节省每个结点所占用的空间,在每个结点中只设置两个链域,令左链指向最左儿子,右链指向紧挨它的右兄弟,树的这种存储表示方法称为树的左右链表示法,也称为树的左兄弟右儿子表示法或者树的二叉链表表示法。这种表示法很容易实现由较小的树来构造较大的树。


这里写图片描述

  其存储结构为:

struct 
{
    datatype data;
    int leftchild;
    int rightsibling;
}cellspace[maxnodes];


这里写图片描述

  用树的左右链表示法,很容易实现除Parent操作以外的各种操作。如果希望有效地实现Parent操作,即快速地找到一个结点的父结点,则在每个结点中增加指向父结点的链域。

struct
{
    datatype data;
    int leftchild;
    int rightsibling;
    int parent;
}cellspace[maxnodes];

  此外,还可以选择这样一种结构:令最右儿子的rightsibling域指向右父亲,于是必须对每个结点增加一个标志位,用于区别其rightsibling域是指向兄弟还是指向父亲。在求一个结点的父亲时,只需沿着rightsibling域查到虚线的指向即可。这比在每一个结点中增加parent域可节省控件,但花费的时间稍多一些。


  • 树不能为空的理由

  如果在设计的体系中定义了森林,同伙私又允许树为空, 那么由于森林是由若干颗树构成的,很可能出现一个集合(森林)包含许多空集(树),或者一个空集(森林)包含许多个空集(树)的情况。如果允许树为空,则很难确定森林中的一颗空树对应于什么样的二叉树,两颗空树对应于什么样的二叉树。

3.树的实现

源码参见树的实现

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值