树和森林
- 树:一对多的结构(可1对0,1对1,1对多),有一个起点 ‘根结点’
 - 结点:树的一个数据元素
 - 孩子:1对多里的 ‘多’
 - 子树:以某个孩子结点为根的一棵树
 - 叶子结点:没有孩子的结点
 - 森林:多棵树

 
二叉树
- 二叉树:每个结点至多有两个孩子(可以1个或0个),分别称为左孩子和右孩子
 - 左孩子(若有)是左子树的根,右孩子(若有)是右子树的根
 - 高度(深度):最深的叶子结点所在层数 

 
二叉树的重要性质
- 第i层至多有2 ^ (i - 1)个结点
 - 高度为h的树至多有2 ^ h - 1个结点

 
两种特殊二叉树
-  
满二叉树:装满的二叉树,高为h => 有2 ^ h - 1个结点

 -  
完全二叉树:只在最下一层的最右边有空缺

 
树/森林转换为二叉树
-  
树转为二叉树:
- 每个结点只保留第一个孩子(老大)作为左孩子,剩下的孩子(老大的兄弟们)依次接到老大的右孩子链上
 
 -  
森林转为二叉树:
- 各树分别转为二叉树
 - 各树根用右孩子链相连
 
 

二叉树顺序存储实现
- 顺序二叉树(底层是数组)
 - 顺序树中结点i的左右孩子分别是2i + 1和2i + 2(i从0开始计数)
 - 若结点为空,使用特殊值(如0)表示 

 
二叉树链式实现
- 链式树(二叉链表)

 
/*二叉树数据结构定义*/
typedef struct TreeNode
{
	int data;
	struct TreeNode *left;
	struct TreeNode *right;
} TreeNode; 
 
二叉树的遍历
- 遍历:按某种确定的次序逐个访问所有结点,时间复杂度是O(n)
 - 先序遍历:当前结点-左子树-右子树
 - 中序遍历:左子树-当前结点-右子树
 - 后序遍历:左子树-右子树-当前结点
 - *层序遍历:逐层从左向右遍历各个结点
 
二叉树先序/中序/后序遍历
- 注意:右孩子是右子树的根,左孩子是左子树的根
 - 注意:一棵树是用其根结点表示的,因从根结点出发我们足以访问整棵树


 
先序遍历序列:A B C
 中序遍历序列:B A C
 后序遍历序列:B C A
 
 先序遍历序列:3 5 4 1 9 8 2 7
 中序遍历序列:5 4 1 3 8 9 2 7
 后序遍历序列:1 4 5 8 7 2 9 3
哈夫曼树与哈夫曼编码
- 先学如何构建一颗哈夫曼树,在学为什么要有哈夫曼树!
 - 初始时,一堆独立结点,结点有自己的权值
 - 重复地让当前权值最小的两个根结点作为左右孩子,生成新的根结点,新结点权值为他们的权值之和,直至形成一颗二叉树

第一步:

第二步:

第三步:

第四步:

第五步:

第六步:

 
Why哈夫曼树?
- 越靠近根节点,权值越大
 - 初始结点全是后来的叶子结点
 - 叶子结点权值越大,离根结点越近 => 路径越短
 - 有什么好处?如果把路径标上0和1…
 - 每一个叶子结点有一个唯一编码(不定长)
 - 思考:如果权值表示在文章中的出现频率,这种编码有什么优势?
 - 最大程度节省空间!越常用的字符码长越短。
 - 这就是哈夫曼编码
 - 题型:给一个字符-频率表,构造哈夫曼树来求哈夫曼编码表
 











