1. 树形结构
1. 概念
树是⼀种⾮线性的数据结构,它是由n(n>=0)个有限结点组成⼀个具有层次关系的集合。把它叫做树是因为它看起来像⼀棵倒挂的树,也就是说它是根朝上,⽽叶朝下的。它具有以下的特点:
•
有⼀个特殊的结点,称为根结点,根结点没有前驱结点
•
除根结点外,其余结点被分成M(M > 0)个互不相交的集合T1、T2、......、Tm,其中每⼀个集合Ti
(1 <= i <= m) ⼜是⼀棵与树类似的⼦树。每棵⼦树的根结点有且只有⼀个前驱,可以有0个或多个后继
•
树是递归定义的
注意:树形结构中,⼦树之间不能有交集,否则就不是树形结构

结点的度:⼀个结点含有⼦树的个数称为该结点的度; 如上图:A的度为6
树的度:⼀棵树中,所有结点度的最⼤值称为树的度; 如上图:树的度为6
叶⼦结点或终端结点:度为0的结点称为叶结点; 如上图:B、C、H、I...等节点为叶结点
双亲结点或⽗结点:若⼀个结点含有⼦结点,则这个结点称为其⼦结点的⽗结点; 如上图:A是B的⽗结点
孩⼦结点或⼦结点:⼀个结点含有的⼦树的根结点称为该结点的⼦结点; 如上图:B是A的孩⼦结点
根结点:⼀棵树中,没有双亲结点的结点;如上图:A
结点的层次:从根开始定义起,根为第1层,根的⼦结点为第2层,以此类推
树的⾼度或深度:树中结点的最⼤层次; 如上图:树的⾼度为4
⾮终端结点或分⽀结点:度不为0的结点; 如上图:D、E、F、G...等节点为分⽀结点
兄弟结点:具有相同⽗结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点
堂兄弟结点:双亲在同⼀层的结点互为堂兄弟;如上图:H、I互为兄弟结点
结点的祖先:从根到该结点所经分⽀上的所有结点;如上图:A是所有结点的祖先
⼦孙:以某结点为根的⼦树中任⼀结点都称为该结点的⼦孙。如上图:所有结点都是A的⼦孙
森林:由m(m>=0)棵互不相交的树组成的集合称为森林
2. 树的表⽰形式
双亲表⽰法,孩⼦表⽰法、孩⼦双亲表⽰法、孩⼦兄弟表⽰法
//孩⼦兄弟表⽰法
class Node{
int val;//存储树节点的数据
Node firstChild;//指向第一个孩子
Node nextBrother;//指向兄弟节点
}
2. 二叉树
1. 概念
⼀棵⼆叉树是结点的⼀个有限集合,该集合:
1.
或者为空
2.
或者是由⼀个根节点加上两棵别称为左⼦树和右⼦树的⼆叉树组成
注意:对于任意的⼆叉树都是由以下⼏种情况复合⽽成

1.
⼆叉树不存在度⼤于2的结点
2.
⼆叉树的⼦树有左右之分,次序不能颠倒,因此⼆叉树是有序树
2 两种特殊的⼆叉树
1.
满⼆叉树: ⼀棵⼆叉树,如果每层的结点数都达到最⼤值,则这棵⼆叉树就是满⼆叉树。也就是说,如果⼀棵⼆叉树的层数为K,且结点总数是
-1 ,则它就是满⼆叉树。
2.
完全⼆叉树: 完全⼆叉树是效率很⾼的数据结构,完全⼆叉树是由满⼆叉树⽽引出来的。对于深度为K的,有n个结点的⼆叉树,当且仅当其每⼀个结点都与深度为K的满⼆叉树中编号从0⾄n-1的结点⼀ 对应时称之为完全⼆叉树。 要注意的是满⼆叉树是⼀种特殊的完全⼆叉树。

3. ⼆叉树的性质
1.
若规定根结点的层数为1,则⼀棵⾮空⼆叉树的第i层上最多有
(i>0)个结点
2.
若规定只有根结点的⼆叉树的深度为1,则深度为K的⼆叉树的最⼤结点数是
(k>=0)
3.
对任何⼀棵⼆叉树, 如果其叶结点个数为 n0, 度为2的⾮叶结点个数为 n2,则有n0=n2+1

4.
当二叉树的总结点个数为偶数时,n1=1;
当二叉树的总结点个数为奇数时,n1=0。(n1即节点有一条边)
5.
具有n个结点的完全⼆叉树的深度k为
向上取整
6.
对于具有n个结点的完全⼆叉树,如果按照从上⾄下从左⾄右的顺序对所有节点从0开始编号,则对于序号为i的结点有:
◦
若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,⽆双亲结点
◦
若2i+1<n,左孩⼦序号:2i+1,否则⽆左孩⼦
◦
若2i+2<n,右孩⼦序号:2i+2,否则⽆右孩⼦

4. ⼆叉树的存储
⼆叉树的存储结构分为:顺序存储和类似于链表的链式存储
⼆叉树的链式存储是通过⼀个⼀个的节点引⽤起来的,常⻅的表⽰⽅式有⼆叉和三叉表⽰⽅式,
/ 孩⼦表⽰法
class Node {
int val; // 数据域
Node left; // 左孩⼦的引⽤,常常代表左孩⼦为根的整棵左⼦树
Node right; // 右孩⼦的引⽤,常常代表右孩⼦为根的整棵右⼦树
}
// 孩⼦双亲表⽰法
class Node {
int val; // 数据域
Node left; // 左孩⼦的引⽤,常常代表左孩⼦为根的整棵左⼦树
Node right; // 右孩⼦的引⽤,常常代表右孩⼦为根的整棵右⼦树
Node parent; // 当前节点的根节点
}
5. ⼆叉树遍历
所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做⼀次且仅做⼀次访问。访问结点所做的操作依赖于具体的应⽤问题(⽐如:打印节点内容、节点内容加1)。
层序遍历
层序遍历就是从所在⼆叉树的根节点出发,⾸先访问第⼀层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,⾃上⽽下,⾃左⾄右逐层访问树的结点的过程就是层序遍历。

• NLR:前序遍历(Preorder Traversal 亦称先序遍历)⸺访问根结点--->根的左⼦树--->根的右⼦树。(根左右)• LNR:中序遍历(Inorder Traversal)⸺根的左⼦树--->根节点--->根的右⼦树。(左根右)• LRN:后序遍历(Postorder Traversal)⸺根的左⼦树--->根的右⼦树--->根节点。(左右根)
// 前序遍历
void preOrder(Node root);
// 中序遍历
void inOrder(Node root);
// 后序遍历
void postOrder(Node root);
前序遍历结果:1 2 3 4 5 6
中序遍历结果:3 2 1 5 4 6
后序遍历结果:3 2 5 6 4 1
1.
已知前序遍历和中序遍历,求后序遍历
步骤:
-
前序遍历的第一个结点是根结点。
-
在中序遍历中找到根结点的位置,根结点左侧是左子树的中序遍历,右侧是右子树的中序遍历。
-
根据左子树和右子树的结点数量,在前序遍历中划分左子树和右子树的前序遍历
-
递归处理左子树和右子树。
2. 已知中序遍历和后序遍历,求前序遍历
-
步骤:
-
后序遍历的最后一个结点是根结点。
-
在中序遍历中找到根结点的位置,根结点左侧是左子树的中序遍历,右侧是右子树的中序遍历。
-
根据左子树和右子树的结点数量,在后序遍历中划分左子树和右子树的后序遍历。
-
递归处理左子树和右子树。
-
3. 已知前序遍历和后序遍历,无法唯一确定中序遍历
-
只有在二叉树是满二叉树或真二叉树(每个结点都有 0 或 2 个孩子)时,才能唯一确定中序遍历。

例题
110. 平衡二叉树 - 力扣(LeetCode)(绝对值<1&&左右树平衡)