· CSDN的uu们,大家好。这里是C语言数据结构的第九讲。
· 目标:前路坎坷,披荆斩棘,扶摇直上。
· 博客主页: @姬如祎
· 收录专栏: 数据结构与算法
目录
1. 树的定义及相关概念
树(Tree)是 n(n >= 0)个节点的有限集。n = 0 时称为空树。在任意一颗非空树中:
①:有且仅有一个特定的称为根的节点(Root);
②:当 n > 1 时,其余节点可以分为 m 个互不相交的有限集,其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)。
树的相关概念:
节点的度:一个节点含有的子树的个数称为该节点的度。如上图:A的为6。
叶节点或终端节点:度为0的节点称为叶节点;如上图:B、C、H、I...等节点为叶节点。
非终端节点或分支节点:度不为0的节点;如上图:D、E、F、G...等节点为分支节点。
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;如上图:A是B的父节点 。孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;如上图:B是A的孩子节点。
兄弟节点:具有相同父节点的节点互称为兄弟节点;如上图:B、C是兄弟节点。
树的度:一棵树中,最大的节点的度称为树的度;如上图:树的度为6。
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推。树的高度或深度:树中节点的最大层次;如上图:树的高度为4。
节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先。
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
森林:由m(m>0)棵互不相交的多颗树的集合称为森林。(数据结构中的学习并查集本质就是一个森林)
对于树的定义需要注意如下两点:
①:n > 0 时,根结点时唯一的,不可能存在多个根结点。
②:m > 0时,子树的个数没有限制,但是他们一定是互不相交的。下图中的两棵树都是不符合树的定义的。(有趣一点的理解就是:想与你做兄弟你却想当父亲)
2. 树的表示方法
2.1 孩子兄弟表示法
一个数据域用来存储数据。两个指针域:一个用来指向他的孩子,另一个用来指向他的兄弟。没有孩子或兄弟指向空即可。这种表示方法很厉害,十分佩服。
2.2 双亲表示法
将节点信息存储在数组中,值指向双亲的下标,以此来表示各节点之间的关系。
R的值为-1,不是一个有效的下标,代表其为根结点。
A的值为0,代表A的双亲是下标为0的节点。
F的值为3,代表F的双亲是下标为3的节点。
以此类推······
树的结构比较复杂,我们重点学习的是一种特殊的树形结构:二叉树。
3. 二叉树的定义
二叉树(BinaryTree)是 n (n >= 0) 个节点的有限集合,该集合或者为空集(称为空二叉树),或者有一个根结点和两颗互不相交的,分别称为根节点的左子树和右子树的的二叉树构成。
特殊的二叉树:
斜数:所有的节点都只有左子树的二叉树叫左斜树。所有节点都只有右子树的二叉树叫右斜树。这两者统称为斜树。
满二叉树:在一棵二叉树中,如果所有的分支节点都存在左子树和右子树,并且所有叶子节点都在同一层上,这样的二叉树称为满二叉树。
完全二叉树:对一棵具有 n 个节点的二叉树按层序编号,如果编号为 i (1 <= i <= n) 的节点与同样深度的满二叉树中编号为 i 的节点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。
通过对上图的观察,我们能够的出如下规律:
①:满二叉树一定是一棵完全二叉树,但完全二叉树不一定是满二叉树。
②:完全二叉树的所有节点与同样深度的满二叉树,他们按层序编号相同的节点,是一一对应的。
③:满二叉树的叶子节点均只能出现在最下两层。
④:最下一层的叶子节点一定集中在做不连续位置。
⑤:如果节点的度为1,则该节点只存在做孩子,即不存在只有右孩子的情况。
⑥:同样节点的树中完全二叉树的深度最小。
4. 二叉树的性质
4.1 性质一
性质一:在二叉树的第 k 层至多有2 ^ (k - 1) 个节点 (k >= 1)。
显然节点数最多的情况就是该二叉树为满二叉树的时候:
4.2 性质二
性质二:深度为 K 的二叉树至多有 2 ^ K - 1 个节点 (K >= 1)。
同样节点数最多的情况就是该树为满二叉树的时候。这个时候我们只需要对深度为 K 的满二叉树的每一层节点数量求和就行。根据高中学的等比数列求和公式不难得出此结论。
4.3 性质三
性质三:对于任何一颗二叉树,如果其终端节点数为 N0,度为 2 的节点数为 N2
则 N2 = N0 - 1 或 N0 = N2 + 1。
证明:
①:当二叉树只有一个节点时,整棵树的分支数为 0,在这基础上,每多一个节点,分支数能且只能多 1 ,于是我们得出结论:二叉树的分支总数 = 二叉树的节点个数 N - 1。
②:又因为分支总数 = 度为 1 的节点数 N1 + 度为 2 的节点数 N2 * 2。
即 N - 1 = N1 + N2 * 2。
③:再结合二叉树的定义,二叉树中的节点可以分为度为 1 的节点 N1,度为 2 的节点 N2,度为 0 的 节点 N0,于是我们再得到结论:二叉树的节点个数 N = N0 + N1 + N2,将这个式子带入②中得到的结论即可得到 N0 = N2 + 1。
4.4 性质四
性质四:具有 N 个节点的完全二叉树的深度为 [ Log2 N ] + 1,其中 [ X ] 为高斯函数,代表不大于 X 的最大整数。
证明:
深度为 K 的满二叉树的节点总数为:2 ^ K - 1,根据完全二叉树的定义,深度也为 K 的完全二叉树的节点总数 N 应该大于深度为 K - 1 的满二叉树,同时应该小于等与深度为 K 的满二叉树,即 2 ^ (K - 1) - 1 < N <= 2 ^ K - 1,即 2 ^ (K - 1) <= N <= 2 ^ K - 1< 2 ^ K。根据对数运算:Log2 N - 1 <= K < Log2 N,又因为 K 为整数,所以 K = [ Log2 N ] + 1。
4.5 性质五
性质五:对完全二叉树进行从 0 - N 的层序编号。那么可以得出以下结论:
①:对于任何节点,其编号为 L,如果其左孩子存在,那么他的编号为:2 * L + 1。
②:对于任何节点,其编号为 L,如果其右孩子存在,那么他的编号为:2 * L + 2。
③:对于任何节点,其编号为 L,他的双亲的编号为:(L - 1) / 2 (这里的除为整除)。
了解了二叉树的常见性质,这里有几道小小的练习题:
1. 某二叉树共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为( )
A 不存在这样的二叉树
B 200
C 198
D 199
2.下列数据结构中,不适合采用顺序存储结构的是( )
A 非完全二叉树
B 堆
C 队列
D 栈
3.在具有 2n 个结点的完全二叉树中,叶子结点个数为( )
A n
B n+1
C n-1
D n/2
4.一棵完全二叉树的节点数位为531个,那么这棵树的高度为( )
A 11B 10
C 8
D 12
5.一个具有767个节点的完全二叉树,其叶子节点个数为()
A 383
B 384
C 385
D 386
答案:
1.B
2.A
3.A
4.B
5.B
5. 二叉树的存储结构
5.1 顺序存储
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆(完全二叉树)才会使用数组来存储,关于堆我们后面的章节会专门讲解。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
5.2 链式存储
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链,后面课程学到高阶数据结构如红黑树等会用到三叉链。