哈夫曼树的知识点概述网上有很多了,就不再补充,主要是分享一下自己对一道题的解题思路。
概述:构建哈夫曼树时,先用下标模拟数组,从叶子结点开始往上构建完整棵哈夫曼树。但和普通二叉树不同,叶子结点在1-n (0号位不用,n为输入的字符个数),根结点在 2n-1 ,其他位置为构造出的中介结点。
虽然哈夫曼树构建好了,很多操作树的方法得依赖于下标模拟的指针实现,每次方法调用的参数都得传入整个结构体数组。所以在构建时,多定义一个结构体指针。构建好哈夫曼树之后,每次操作树的时候可以通过传入树根的指针,当作一棵普通的二叉树来操作整棵树。(详细代码在哈夫曼树构建。)
(笔者第一次写博客,内容难免有些浅显或错误之处,还请大家指出一起探讨,共勉。)
走个流程。
1. 树结点结构体。
二叉树可以用下标模拟指针(属性 L、R),也可以自己定义结构体指针(LChild、RChild)。这里笔者为了方便后面的先序输出哈夫曼树存储的字符,就定义了结构体指针。
#define M N * 2 - 1 // 30个字符,计算可得出总节点个数。
#define N 30 // 叶子节点个数。假设最多输入30个字符。
#define MAX 999
// 哈夫曼树结构体。
typedef struct tree {
char ch; // 字符。
double weight; // 权重。
int parent; // 父结点。
int L, R; // 左右子树的下标。
struct tree *LChild; // 左孩子结点指针。
struct tree *RChild; // 右孩子结点指针。
char hashcode[N]; // 字符的哈希编码。
int is_leaves; // 是否是叶子结点标记。
} tree, Htree[M]; // tree 是单个结点,Htree 是结构体数组。
2. 哈夫曼树构建。
// 选择最小权重的两个节点
// 具体代码实现大同小异。找出权重最小的两个结点,标记下标位置,再修改父亲结点参数,下次不再进行参与比较排序。
void Select(Htree ht, int n, int *s1, int *s2) {
int i;
double min1 = MAX, min2 = MAX;
*s1 = 0;
*s2 = 0;
for (i = 1; i <= n; i++) {
if (ht[i].parent == 0) {
if (ht[i].weight < min1) {
min2 = min1;
*s2 = *s1;
min1 = ht[i].weight;
*s1 = i;
} else if (ht[i].weight < min2) {
min2 = ht[i].weight;
*s2 = i;
}
}
}
}
// 构建哈夫曼树
void crateHtree(Htree ht, int n) {
int i;
// 初始化叶子节点。
// 叶子结点从1-n。(0号位不用)
char s[10];
for (i = 1; i <= n; i++) {
ht[i].parent = 0;
ht[i].L = 0;
ht[i].R = 0;
ht[i].is_leaves = 1;
ht[i].RChild = NULL;
ht[i].LChild = NULL;
}
// 初始化树的非叶子节点。 n+1项开始。
for (i = n + 1; i <= 2 * n - 1; i++) {
ht[i].weight = 0;
ht[i].parent = 0;
ht[i].L = 0;
ht[i].R = 0;
ht[i].is_leaves = 0;
ht[i].RChild = NULL;
ht[i].LChild = NULL;
}
// 开始构建哈夫曼树。
// 和平时的构建树方法不同,从下往上构建,新的结点为现有结点的父亲结点,新结点的左右孩子指针指向原有参与构建的两个结点。
int s1