07. 树

发布时间 2023-03-27 18:51:42作者: 夏冰翎

一、什么是树

  (Tree)是 n(n ≥ 0)个结点构成的有限集合。当 n=0 时,称为 空树。对于任一棵 非空树(n > 0),它具备以下性质:

  • 树中有一个称为“(Root)”的特殊结点,用 r 表示;
  • 其余结点可分为 m(m > 0)个 互不相交的 有限集 \(T_{1}\)\(T_{2}\),……,\(T_{m}\),其中每个集合本身又是一棵树,称为原来树的“子树(SubTree)”;

img

子树是不相交的;

除了根节点外,每个结点有且只有一个父节点;

一颗 N 个结点的树有 N-1 条边;

二、树的常用术语

  1. 结点的度(Degree):结点的子树个数;
  2. 树的度:树的所有结点中最大的度数;
  3. 叶结点(Leaf):度为 0 的结点;
  4. 父结点(Parent):有子树的结点是其子树的根节点的父节点;
  5. 子结点(Child):若 A结点 是 B结点 的父节点,则称 B结点 是 A结点的子节点;子节点也称 孩子结点
  6. 兄弟结点(Sibling):具有同一父节点的各结点彼此是兄弟结点;
  7. 路径和路径长度:从结点 \(n_{1}\)\(n_{k}\) 的路径为一个结点序列 \(n_{1}\)\(n_{2}\),……,\(n_{k}\)\(n_{i}\)\(n_{i+1}\) 的父节点。路径所包含边的个数为 路径的长度
  8. 祖先结点(Ancestor):沿 树根到某一结点路径 上的所有结点都是这个结点的 祖先结点
  9. 子孙结点(Descendant):某一结点的 子树中的所有结点 就是这个结点的子孙。
  10. 结点的层次(Level):规定 根节点在 1 层,其它任一结点的层数是其父节点的层数加 1;
  11. 树的深度(Depth):树中所有结点的 最大层次 是这棵树的深度。

三、树的表示方法

  树一般我们采用 儿子—兄弟表示法,即一个结点有两个指针域,一个指向它的子节点,另一个指向它的兄弟结点。

img

用儿子—兄弟表示法的树旋转 45° 可以看成 二叉树;

四、集合的表示

  我们可以用 树结构 表示集合,树的每个结点代表一个集合元素。例如,有三个整数集合,S1 = {1,2,4,7},S2 = {3,5,8},S3={6,9,10},它们的表示方法如下:

img

双亲表示法:孩子指向双亲;

  我们可以采用数组存储形式,数组中每个元素的类型描述为:

typedef struct
{
    ElementType Data;
    int Parent;
}SetType;

img

五、集合运算

【1】、查找某个元素所在的集合(用根结点表示)

  在数组 S 中查找值为 X 的元素所属的集合。MaxSize 是全局变量,为数组 S 的最大长度。

int Find(SetType s[], ElementType X)
{
    int i;

    for(i = 0; i < MaxSize && S[i].Data != X; i++);
    if(i >= MaxSize)
    {
        return -1;          // 未找到,返回-1
    }
    for(; S[i].Parent >= 0; i = S[i].Parent);
    return i;               // 找到X所属集合,返回树根结点在数组S中的下标
}

【2】、集合的并运算

  分别找到 X1 和 X2 两个元素所在集合树的 根节点,如果它们不同根,则将其中的 一个根节点的父节点指针设置为另一个根节点的数组下标

viud Unino(SetType S[],ElementType X1,ElementType X2)
{
    int Root1,Root2;

    Root1 = Find(S,X1);
    Root2 = Find(S,X2);

    if(Root1 != Root2)
    {
        S[Root2].parent = Root1;
    }
}

为了改善合并以后的查找性能,可以采用小的集合合并到相对大的集合中。