森林、树与二叉树
一、转化思想及遍历
//其根结点的右子树总为空
- 在兄弟之间加一连线
- 对每个结点,只保留与第一个孩子的连线,去除其他孩子之间的连线
- 以树的根节点为轴心,顺时针转 45°
二、二叉排序树
1. 定义
二叉排序树(二叉查找树)或者是一颗空树,或者是具有下列特性的二叉树。
(1)若左子树非空,则左子树上所有结点的值均小于根结点的值;
(2)若右子树非空,则右子树上所有结点的值均大于根结点的值;
(3)左、右子树也分别是一棵二叉排序树。
根据二叉排序树的定义,左子树结点值 < 根结点值 < 右子树结点值,所以对二叉排序树进
行中序遍历,可以得到一个 递增 的有序序列。
2. 插入操作
- 若原二叉排序树为空,则直接插入结点;否则,若关键字k小于根结点值,则插入到左子树,若关键字k大于根结点值,则插入到右子树。
//则向二叉排序树中插入一个新结点时,新结点一定会成为该树的一个叶子结点
3. 删除操作
(1)若被删除的结点是叶子结点,则直接删除;
(2)若结点只有一棵左子树或者右子树,则让该结点的子树成为其父结点的子树;
(3)若结点有左、右两棵子树,则今该结点的直接后继(直接前驱)代替该结点,然后从二叉排序树中删去这个直接后继(直接前驱),这样就转换成了前两种情况。
三、 哈夫曼树
1.定义:
树中结点常被赋予一个代表某种意义的数值,那个数值称为该结点的权。
- 结点的带权路径长度:从树的根到任意结点的路径长度(经过的边数)与该结点上权值的乘积。
- 树的带权路径长度:树中所有叶结点的带权路径长度之和,记为WPL一三wh.
- 哈夫曼树:带权路径长度最小的二叉树,也称最优二叉树。
2. 构造算法
// 哈夫曼树结点的定义
struct HuffmanNode {
char character; // 待编码的符号
double weight; // 符号出现的频率
int parent, lchild, rchild; // 父结点、左, 右孩子结点的位置};
class HuffmanTree {
vector<HuffmanNode> hufftree; // 树中所有结点的存储空间
int n; // 叶子结点数
public:
HuffmanTree(vector<HuffmanNode> &leafs);
…
};
//Huffman树的构造函数
HuffmanTree::HuffmanTree(vector<HuffmanNode> &leafs) {
n = leafs.size();
hufftree.resize(2*n-1);
for(int i=0; i<n; i++) { // 初始化 n 个二叉树
hufftree[i].character = leafs[i].character;
hufftree[i].weight = leafs[i].weight;
hufftree[i].parent = hufftree[i].lchild = hufftree[i].rchild = -1; }
for( i = n; i < 2*n-1; i++) {
int least, less;
SelectSmall(least, less, i); // 找到最小、次小的根结点下标
hufftree[least].parent = hufftree[less].parent = i;
hufftree[i].parent = -1;
hufftree[i].lchild = least;
hufftree[i].rchild = less;
hufftree[i].weight = hufftree[least].weight + hufftree[less].weight;
}
}
3. 例题
-
有一电文使用五种字符 a, b, c, d, e 中,其出现频率依次为4.7.5.2.9。
(1)试画出对应的哈夫曼树(要求左子树根结点的权小于等于右子树根结点的权)。
(2)求出每个字符的哈夫曼编码。
(3)译出编码序列11000111000101011的相应电文。
(4)求带权路径长度。
解: