基本数据结构 -- 树简介

1、什么是树算法

  树是一个有限结点组成的集合。能够用递归的方式来定义一棵树:树能够是一个空集,若非空,则一棵树由一个根(root)结点 r 以及 0 个或多个非空的(子)树 T一、T二、T三、...,Tk 组成,这些子树中的每一棵的根都被来自根 r 的一条有向的边(edge)所链接。每一棵子树的根叫作根 r 的儿子(child),而 r 是每一棵子树的根的父亲(parent)数据结构

树的几个概念:spa

1)树叶(leaf):从上述的递归定义能够知道,一棵树是由 N 个结点和 N-1 条边组成的集合。每一个结点均可以有零个或任意多个儿子,没有儿子的结点称为树叶(leaf);指针

2)兄弟(sibling):具备相同父亲的结点称为兄弟(sibling);code

3)路径(path):从结点 n1 到 nk 路径定义为结点 n1、n2、n3、...、nk 的一个序列,使得对于 1 <= i  <= k,结点 ni 是 ni+1 的父亲。这个路径的长为该路径上的边的条数,即 k-1。从每个结点到它本身有一条长为 0 的路径。在一棵树中,从根到每一个结点刚好存在一条路径blog

4)深度(depth):对于任意结点 ni,ni 的深度为从根到 ni 的惟一路径的长,根的深度为0;排序

5)高(height):对于任意结点 ni,ni 的高为从 ni 到一片树叶的最长路径的长,树的高等于它的根的高(只有一个根结点的树的高度为0,空树的高度为 -1);递归

6)结点的度:一个结点的儿子的个数。内存

图 1-1 树io

  如图所示,为一棵树,其中,A是根结点;B、C、D、E、F 为 A 的儿子,A 为它们的父亲;B 和 C有相同的父亲,它们是兄弟(sibling);从结点 A 到结点 M 的路径为 A、D、H、M;结点 B 的深度为1;结点 D 的高度为 3;整棵树的高度为 4。

 

2、二叉树

  二叉树是一棵树,其中每一个结点最多有两个儿子,分别为左子结点和右子结点。二叉树的平均深度是 O(√N)。

2.1 二叉树的实现

  由于一棵二叉树的每一个结点最多只能有两个子结点,故而咱们能够用指针直接指向它们。树结点的声明在结构上相似于双链表的声明,在声明中,一个结点由关键字(Key)和两个指向其余结点的指针(Left 和 Right)组成:

typedef int ElementType;

struct TreeNode {
    ElementType Element;
    struct TreeNode *Left;
    struct TreeNode *Right;
};

typedef struct TreeNode *PtrToNode;
typedef PtrToNode BinaryTree;

  这段代码声明了一个结构,该结构包含一个 ElementType 类型的数据,用于保存结点数据;一个 Left 指针,指向结点的左子树;和一个 Right 指针,指向结点的右子树。

 

2.2 二叉树的遍历

  按必定的顺序,依次访问二叉树中的全部结点的操做称为遍历。有三种方式能够对二叉树进行遍历,如下面这幅图为例进行介绍:

图 2-1 二叉树

1)先序遍历

  先访问根结点,而后遍历左子树,再遍历右子树。对图 2-1 所示的二叉树进行先序遍历结果为:A-B-D-G-H-E-C-F-I-J。 先序遍历一棵二叉树的C语言实现以下:

/* 先序遍历,递归实现 */
void PreOrderRecursion(BinaryTree T)
{
    if (T != NULL) {
        printf("%d\t", T->Element);
        PreOrderRecursion(T->Left);
        PreOrderRecursion(T->Right);
    }
}

  上述代码用递归的方式实现了先序遍历二叉树。须要注意的是判断条件,要时刻判断二叉树是否为空,这在递归中尤其重要。递归实现先序遍历的代码十分简洁且易懂。可是,递归调用的空间复杂度较大,当输入规模很大时,会占用至关多的内存,也容易形成堆栈的溢出。

  可使用非递归的方式来实现先序遍历,大体思想以下:首先,将根结点保存到一个数据结构里,而后访问左子树,待左子树访问完后,取出根结点,再访问右子树;每访问一个子树以前,都将这棵子树的根结点保存,待须要访问其右子树时再取出。能够知道的是,先保存进去的结点数据后取出,是一个后入先出结构,所以使用栈来保存根结点的数据十分合适。

 

2)后序遍历

  先遍历左子树,再遍历右子树,最后访问根结点。对图 2-1 所示的二叉树进行后序遍历的结果为:G-H-D-E-B-I-J-F-C-A。

/* 后序遍历,递归实现 */
void PostOrderRecursion(BinaryTree T)
{
    if (T != NULL) {
        PostOrderRecursion(T->Left);
        PostOrderRecursion(T->Right);
        printf("%d\n", T->Element);
    }
}

 

3)中序遍历

  先遍历左子树,而后访问根结点,再遍历右子树。对图 2-1 所示的二叉树进行中序遍历的结果为:G-D-G-B-E-A-C-I-F-J。

/* 中序遍历,递归实现 */
void InOrderRecursion(BinaryTree T)
{
    if(T != NULL){
        InOrderRecursion(T->Left);
        printf("%d\n",T->Element);
        InOrderRecursion(T->Right);
    }
}

 

3、一些特殊的二叉树

3.1 满二叉树

  满二叉树是一棵深度为 k,且有 2^(k-1) 个结点的二叉树,以下图所示:

图 2-2 满二叉树

  这就是一个满二叉树,能够看到,满二叉树的全部叶子都在同一层  ——  最后一层,非叶子结点的度必定为2。在一样深度的二叉树中,满二叉树的结点数是最多的,叶子数也是最多的。

 

3.2 彻底二叉树

  彻底二叉树,除了最后一层外,其他层都是满的,且最后一层的叶子结点都几种在树的左边。满二叉树就是彻底二叉树的一个特例。以下图:

图 2-3 彻底二叉树

 

3.3 二叉查找树(二叉排序树)

  二叉查找树是二叉树的一种,更适合于进行查找操做。对于二叉查找树,树中的每一个结点 X 的左子树中全部关键字的值小于 X 的关键字值,而它的右子树中全部关键字值大于 X 的关键字值。这意味着,该树的全部元素能够以某种统一的方式进行排序。二叉查找树的平均深度是 O(logN)。 

 

图 2-4 二叉查找树

 

参考资料:

《算法导论 第三版》

《数据结构与算法分析--C语言描述》

相关文章
相关标签/搜索