树
树的概念
树是一种非线性的数据结构,它是由n (n>=0) 个有限节点组成的具有层次关系的集合。数据结构中的“树”就像一棵倒挂的树,根在上面,枝干和叶子朝下。
树的特点:
- 有一个特殊的节点,叫做根节点,根结点没有前驱结点
- 树是递归定义的。除了根节点以外,其余节点被分成M个互不相交的集合T1,T2……Tn,其中每个集合又是一棵树,可以将其称之为子树,每棵子树的根节点有且仅有一个前驱,可以有0个或多个后继节点。
- 子树之间互不相交
- 除了根节点以外,每个节点有且仅有一个父节点
- 一个具有n个节点的树有n-1条边(可以这么理解:共有n个节点,除了根节点以外,对于剩余的n-1个节点,每个节点上面都有且仅有一条边将它与前驱节点连接,所以共有n-1条边)
树形结构中,子树之间不能有交集
非树形结构:
树的相关术语
- 父结点 / 双亲结点:若一个结点含有子结点,则这个结点称为其子结点的父结点;如上图:A 是 B 的父结点
- 子结点 / 孩子结点:一个结点含有的子树的根结点称为该结点的子结点;如上图:B 是 A 的孩子结点
- 结点的度:一个结点有几个孩子,他的度就是多少;比如 A 的度为 6,F 的度为 2,K 的度为 0
- 树的度:一棵树中,最大的结点的度称为树的度;如上图:树的度为 6
- 叶子结点 / 终端结点:度为 0 的结点称为叶结点;如上图:B、C、H、I... 等结点为叶结点
- 分支结点 / 非终端结点:度不为 0 的结点;如上图:D、E、F、G... 等结点为分支结点
- 兄弟结点:具有相同父结点的结点互称为兄弟结点 (亲兄弟);如上图:B、C 是兄弟结点
- 结点的层次:从根开始定义起,根为第 1 层,根的子结点为第 2 层,以此类推;
- 树的高度或深度:树中结点的最大层次;如上图:树的高度为 4
- 结点的祖先:从根到该结点所经分支上的所有结点;如上图:A 是所有结点的祖先
- 路径:一条从树中任意节点出发,沿父节点 - 子节点连接,达到任意节点的序列;比如 A 到 Q 的路径为:A - E - J - Q;H 到 Q 的路径 H - D - A - E - J - Q
- 子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是 A 的子孙
- 森林:由 m(m>0)棵互不相交的树的集合称为森林;
树的表示
孩子兄弟表示法:
树是非线性结构,相对于线性结构更加复杂,如果想要表示也更加麻烦,既需要保存值域,又要能够表示节点与节点的关系,树的前驱节点有且仅有一个,但是后继结点可能有多个。
实际中树有很多种表示方式式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等。我们这里就简单的了解其中最常用的孩子兄弟表示法:
struct TreeNode {
int data; // 节点数据
struct TreeNode *Child; // 指向左边开始的第一个孩子
struct TreeNode *Brother; // 指向下一个兄弟(右兄弟)
}
二叉树
树形结构中,二叉树最为常用,一颗二叉树是若干节点的有限集合,这个集合是有一个根节点加上两棵分别称为左子树和右子树(这两棵子树可以为空树)的二叉树构成的。
二叉树特点:
- 二叉树不存在度大于2的节点
- 二叉树的子树有左右之分,次序不能颠倒,二叉树是有序树
满二叉树
一个二叉树,如果每层的节点个数都达到最大值,这个二叉树就是满二叉树。换言之,如果一个二叉树的层数为k, 而且节点个数是2^k-1,那么这个二叉树就是满二叉树。
满二叉树的第k层节点个数是2^(k-1)。
根据满二叉树的特点可知:
- 若规定根结点的层数为 1,则一棵非空二叉树的第 i 层上最多有2^(i-1) 个结点
- 若规定根结点的层数为 1,则深度为 h 的二叉树的最大结点数是 2^h-1
- 若规定根结点的层数为 1,具有 n 个结点的满二叉树的深度h= log2(n+1)
完全二叉树
完全二叉树是指一棵二叉树中,除了最后一层外,其他层的节点都达到最大数量,并且最后一层的节点从左到右连续排列(不能有空缺)。
二叉树的存储结构
二叉树一般可以使用两种结构存储,一种是顺序结构,一种是链式结构。
顺序结构存储的二叉树
顺序结构存储二叉树是指 用数组(或连续内存空间)存储二叉树的节点数据,通过数组下标的关系表示节点之间的父子关系。这种存储方式特别适合 完全二叉树,可以避免指针开销,提高存储和访问效率。
完全二叉树的顺序存储
非完全二叉树的顺序存储
实际中,我们常用顺序结构来存储堆(一种二叉树)中的数据。
链式结构存储二叉树是指 用节点对象(结构体/类)和指针(或引用)动态表示二叉树的父子关系,每个节点存储数据及左右子节点的地址。这是二叉树最直观、最灵活的存储方式,适合任意二叉树(包括非完全二叉树)。
链式存储二叉树的节点结构:
struct BinaryTreeNode
{
int data;
struct BinaryTreeNode* leftchild;
struct BinaryTreeNode* rightchild;
}
这一小节主要是讲解了一些关于二叉树的入门知识,后面我们将深入学习堆、链式存储的二叉树的相关代码实现。对于链式结构的二叉树的实现,我们将体会递归代码的暴力美学,小伙伴们敬请期待吧!