1.树的基本概念
2.二叉树的概念和性质
2.1.二叉树性质
1)结点个数
2)第i层,最多结点个数
3)者深度为k,前k层最多结点个数
4)叶子结点个数
2.2.完全二叉树性质
1)结点个数
2)第i层最多节点个数
3)前i层最多结点个数
4)叶子结点个数
5)所有叶子节点出现在哪?
6)右子树最大层次与左子树最大层次数
7)结点个数n = exp(k)
8)树的深度k和节点个数之间的关系 k = exp(n)
9)对于编号为i的结点,父结点点,右孩子,左孩子编号?
10)度为1的结点个数是几个?
2.3.答案
3.二叉树的存储
重点记忆:二叉链表结点的定义
typedef struct BTNode {
int data;
struct BTNode* Lchild, *Rchild;
}BTNode;
4.二叉树的遍历
/*以下是逻辑代码,并不能具体执行*/
typedef struct BTNode {
int data;
struct BTNode* Lchild, *Rchild;
}BTNode, TreeNode;
/**
* 先序遍历
* 顺序:左结点,根,右结点
*/
void preorder_traversal(TreeNode *root) {
if (root == nullptr)
return;
printf("%d,", root->data);//根
preorder_traversal(root->Lchild);
preorder_traversal(root->Rchild);
}
/**
* 中根序遍历
* 顺序:左,根,右
*/
void inorder_traversal(TreeNode* root) {
if (root == nullptr)
return;
inorder_traversal(root->Lchild);
printf("%d,", root->data);
inorder_traversal(root->Rchild);
}
/**
* 后根序遍历
* 顺序:左,右,根
*/
void postorder_traversal(TreeNode* root) {
if (root == nullptr)
return;
inorder_traversal(root->Lchild);
inorder_traversal(root->Rchild);
printf("%d,", root->data);
}
重点:
- 已知一棵二叉树如右图,给出对这棵二叉树进行前序、中序、后序、层级遍历的结果序列
- 已知一棵二叉树的先序序列为 ABDGCFK,中序序列为 DGBAFCK,则后序序列为?
- 假设一棵二叉树先序序列为 EBADCFHGIKJ 和中序序列为 ABCDEFGHIJK,画出该树。
5.线索二叉树
遍历本质上来讲是:把非线性结构线性化的操作。
设一棵二叉树有
n
个结点,则有
n-1
条边(指针连线) ,而
n
个结点共有
2n
个指针
域(
Lchild
和
Rchild
) ,显然有
n+1
个空闲指针域未用。则可以利用这些空闲的指针域
来存放结点的直接前驱和直接后继信息。
对结点的指针域做如下规定:
若结点有左孩子,则
Lchild
指向其左孩子,否则,指向其直接前驱;
若结点有右孩子,则
Rchild
指向其右孩子,否则,指向其直接后继;
为避免混淆
,
对结点结构加以改进,增加两个标志域,如图所示。
线索化二叉树:
二叉树的线索化指的是依照某种遍历次序使二叉树成为线索二叉树
的过程。
线索化的过程就是在
遍历过程中修改空指针使其指向直接前驱或直接后继的过程
。
6.树和二叉树的转换
6.1.树转二叉树
转换之后,符合左孩右兄的规律。
二叉树的根结点没有右子树,只有左子树;
左子结点仍然是原来树中相应结点的左子结点,而所有沿右链往下的右子结点均是原
来树中该结点的兄弟结点。
6.2.二叉树转树
6.3.森林转二叉树
转换步骤:
①将
F={T1, T2,.,Tn}
中的每棵树转换成二叉树。
②每棵二叉树作为前一棵二叉树的根结点的右子树,依次类推,则第一棵树的根结点就是转换后生成的二叉树的根结点。
6.4.二叉树转森林
6.5.树的遍历
树转换成二叉树后,
树的先序遍历实质上与将树转换成二叉树后对二叉树的先序遍历相同。
树的后序遍历实质上与将树转换成二叉树后对二叉树的中序遍历相同
树先序 : 二叉树先序
树后序 : 二叉树中序
例子:假设一棵二叉树的中序序列为 DCBGEAHFIJK 和后序序列为 DCEGBFHKJIA。请画
出该树包含的森林。
重点:如何转换,能画出转换后的树或二叉树
7.哈夫曼树
7.1.定义
①结点路径:从树中一个结点到另一个结点的之间的分支构成这两个结点之间的路径。
②路径长度:结点路径上的分支数目称为路径长度。
③权(值):各种开销、代价、频度等的抽象称呼。
④结点的带权路径长度:从该结点的到树的根结点之间的路径长度与结点的权(值)的乘积
⑤ 树的路径长度:从树根到每一个结点的路径长度之和。
⑥树的带权路径长度:树中所有叶子结点的带权路径长度之和
Huffman 树:具有 n 个叶子结点(每个结点的权值为 wi) 的二叉树不止一棵,但在所有的这些二叉树中,必定存在一棵 带权路径长度最小的树,称这棵树为 Huffman 树(或称最优树)
7.2.哈夫曼树构造过程(重点)
①从小到大排序
②将最小的两个节点合并,回到步骤①
循环完成两个步骤即可。
7.3.Huffman 编码方法
由于每个字符都是叶子结点,不可能出现在根结点到其它字符结点的路径上,所以
一
个字符的 Huffman 编码不可能是另一个字符的 Huffman 编码的前缀
。
以字符集 C 作为叶子结点,次数或频度集 W 作为结点的权值来构造 Huffman 树。规定 Huffman 树中左分支代表“0”,右分支代表“1” 。从根结点到每个叶子结点所经历的路径分支上的“0”或“1”所组成的字符串,为该叶子结点所对应的编码,称之为 Huffman 编码。
若字符集
C={a, b, c, d, e, f}
所对应的权值集合为
W={8, 3, 4, 6, 5, 5}
,如图所示,则字符
a,b, c,d, e,f
所对应的
Huffman
编码分别是:
10
,
010
,
011
,
00
,
110
,
111
。
7.4.总结
- 哈夫曼树只有 0 度结点和 2 度结点(注意:如果是度为m的哈夫曼树,那么只有和);
- 哈夫曼树的 WPL 值最小, ,其中是权重,是路径长度
- 哈夫曼树不唯一,因为哈夫曼树的左右子树可以交换,但是 WPL 值唯一
- 哈夫曼树本质上不属于二叉树,但是考试中我们认为哈夫曼树是二叉树
- 哈夫曼树的上层结点权值不小于下层结点的权值
- 哈夫曼编码只讨论叶子的编码
常见题型:
1)设一组权值集合
W=
(
15
,
3
,
14
,
2
,
6
,
9
,
16
,
17
),要求根据这些权值集合
构造一棵哈夫曼树,则这棵哈夫曼树的带权路径长度为
2)平均编码长度公式 , 其中wi是叶子结点权值,li是路径长度。
3)
若度为
m
的哈夫曼树中,其叶结点个数为
n
,则非叶结点的个数为?
该哈夫曼树只有两种结点,
只有和。
叶子结点公式:
因此,
8.并查集
并查集(
Union-findSets
)是一种非常精巧而实用的数据结构,他主要用于处理一些不
相交集合的合并问题。一些常见的用途有求连通子图、求最小生成树的
Kruskal
算法和求
最近公共祖先(
LeastCommonAncestors
,
LCA
)等.