1.啥是哈夫曼树?
哈夫曼树(Huffman Tree)是一种特殊的二叉树,它被广泛应用于数据压缩算法中。它的特点是:权重较大的节点离根节点较近,权重较小的节点离根节点较远。
哈夫曼树在数据压缩中的应用是通过构建最优编码来实现的。每个叶子节点都对应于一个字符或符号,通过从根节点到叶子节点的路径来表示该字符的编码。对于出现频率较高的字符,其编码较短,而对于出现频率较低的字符,其编码较长,从而实现了对数据的压缩。
不扯虚的,直接白话解释:哈夫曼树是一种根据权重构建的二叉树,用于生成最优编码的数据结构,通过权重较小的节点逐步合并来构建,并能够实现对数据的高效压缩。
2.怎么构建呢?
再怎么高级,它都是一个二叉树,都得和二叉树差不多,就是一个结构体,一个初始化的创建函数,就是创建函数比二叉树麻烦一些。
- 哈夫曼树结构体定义:就是把data换了个名,改成weight权重罢了
// 哈夫曼树结点的定义
typedef struct huffmanNode {
int weight;//权重
struct huffmanNode *left;
struct huffmanNode *right;
} HuffmanNode;
- 创建函数:3步走
- 首先我们需要传进的是一个权重数组和该数组的长度,然后根据给定的权重数组创建一组叶子节点,每个叶子节点都包含一个权重值。(正常传值,把权重数组的值传到新建的指针数组中。)
- 然后,通过一个循环构建哈夫曼树,直到只剩下一个根节点。循环的每一次迭代中,找到权重最小的两个节点,并将它们合并为一个新的父节点。根据性质,新的父节点的权重等于两个子节点的权重之和,同时将两个子节点设置为新的父节点的左右子节点。
- 然后,删除原来的两个子节点,并将新的父节点加入节点数组中,并更新节点数量。最后,返回根节点指针。
哈哈哈哈,这三步有点难记,就把代码拆俩半掰扯掰扯吧~
第一步:这步和用指针数组创建普通二叉树一样
//第一步:传值是权重数组和数组长度n
HuffmanNode* createHuffmanTree(int weights[], int n) {
// 循环创建叶子结点:因为传进来的data是一个数组所以需要循环
HuffmanNode* nodes[n];//定义指针数组
//类似于循环输入一个数组
for (int i = 0; i < n; i++) {
HuffmanNode* node = (HuffmanNode*)malloc(sizeof(HuffmanNode));//初始化指针数组的一个项的指针并分配存储单元
//初始化每项:有值的赋值没值的就空
node->weight = weights[i];
node->left = NULL;
node->right = NULL;
//权重数组重每项都这么弄
nodes[i] = node;
}
第2步:使用while循环构建哈夫曼树。每次循环都找出权重值最小的两个节点,创建一个新的父节点,其权重值为这两个节点的权重值之和,并将这两个节点设为新节点的左右子节点。去除原来的两个子节点,并将新的父节点加入节点数组中,并更新节点数量。
//循环条件:权重数组中元素大于2个
while (n > 1) {
//设置两个变量min1和min2,先分别指向权重数组的第一、第二元素下标
int min1 = 0;
int min2 = 1;
//从第三个元素开始,比较权重大小,把小的放前面
for (int i = 2; i < n; i++) {
if (nodes[i]->weight < nodes[min1]->weight) {
min2 = min1;
min1 = i;
} else if (nodes[i]->weight < nodes[min2]->weight) {
min2 = i;
}
}
HuffmanNode* parent = (HuffmanNode*)malloc(sizeof(HuffmanNode));
parent->weight = nodes[min1]->weight + nodes[min2]->weight;//父节点的权重等于前两个之和
parent->left = nodes[min1];
parent->right = nodes[min2];
//将权重值最小的两个节点替换为一个新的父节点,并将原来的两个节点移动到数组的末尾。
if (min1 < min2) {
nodes[min1] = parent;
nodes[min2] = nodes[n-1];
} else {
nodes[min2] = parent;
nodes[min1] = nodes[n-1];
}
n--;
}
第三步:最后,返回根节点指针。
return nodes[0];//返回头