21. 哈夫曼树
一、什么是哈夫曼树
哈夫曼树(Huffman Tree),又称为 最优二叉树 或 最小带权路径长度树,是一种特殊的二叉树。假设二叉树有 n 个叶子结点,每个叶子结点带有权值 ,从根结点到每个叶子结点的长度为 ,则每个叶子结点的 带权路径长度(WPL) 之和就是:。
二、哈夫曼树的构造
- 从小到大进行排序,每一个数据都可以看作一个结点,每个结点可以看作一个最简单的二叉树。
- 取出根结点权值最小的两颗二叉树
- 组成一颗新的二叉树,该二叉树的根结点的权值就是前面两颗二叉树根结点权值的和。
- 再将这个新的二叉树,一根结点的权值大小进行排序,不断重复步骤 1 ~ 4,直到数列中,所有的数据都被处理完。
2.1、最小堆的构建
typedef struct HeapStruct
{
TreeNode * Node;
int Size;
int Capacity;
} HeapStruct, * MinHeap;
#include <stdlib.h>
#include <limits.h>
/**
* @brief 最小堆的创建
*
* @param MaxSize 最大容量
* @return MaxHeap 执行最小堆的指针
*/
MinHeap CreateMinHeap(int MaxSize)
{
MinHeap H = (MinHeap)malloc(sizeof(HeapStruct));
H->Node = (TreeNode *)calloc(sizeof(TreeNode), MaxSize + 1);
H->Size = 0;
H->Capacity = MaxSize;
// 定义哨兵为小于堆中所有可能元素的值,便于后序操作
H->Node[0].Weight = INT_MIN;
H->Node[0].Data = '\0';
H->Node[0].Left = H->Node[0].Right = NULL;
return H;
}
/**
* @brief 插入元素
*
* @param H 最小堆
* @param X 插入的元素
*/
void Insert(MinHeap H, TreeNode * X)
{
int i = 0;
if (H->Size == H->Capacity)
{
printf("堆已满,无法插入元素\n");
return;
}
// i指向插入后堆中的最后一个元素的位置
i = ++H->Size;
// 从最后一个元素开始,依次向上比较,直到找到合适位置
for (; H->Node[i / 2].Weight > X->Weight; i /= 2)
{
H->Node[i] = H->Node[i / 2];
}
// 插入元素,同类型结构体之间可直接赋值操作
H->Node[i] = * X;
}
/**
* @brief 删除元素
*
* @param H 最小堆
* @return ElementType 最大的元素
*/
TreeNode * Delete(MinHeap H)
{
int Parent = 0, Child = 0;
TreeNode * MinItem = NULL, temp = {0};
if (H->Capacity == 0)
{
printf("堆为空,无法删除元素\n");
return NULL;
}
MinItem = (TreeNode *)malloc(sizeof(TreeNode));
MinItem->Weight = H->Node[1].Weight;
MinItem->Left = H->Node[1].Left;
MinItem->Right = H->Node[1].Right;
// 用最小堆中最后一个元素从根结点开始向下过滤下层结点
temp = H->Node[H->Size--];
// Parent指向当前结点,Parent * 2 指向左子结点
for (Parent = 1; Parent * 2 <= H->Size ; Parent = Child)
{
Child = Parent * 2;
// Child != H->Size,意味着有右子结点,Child指向左右子结点的较小者
if (Child != H->Size && (H->Node[Child].Weight > H->Node[Child + 1].Weight))
{
Child++;
}
if (temp.Weight <= H->Node[Child].Weight)
{
break; // 找到合适位置,退出循环
}
else
{
H->Node[Parent] = H->Node[Child]; // 移动temp元素到下一层
}
}
H->Node[Parent] = temp;
return MinItem;
}
2.2、哈夫曼树的构造
typedef struct TreeNode
{
int Weight;
struct TreeNode * Left;
struct TreeNode * Right;
} TreeNode, * HuffmanTree, * Position;
/**
* @brief 构造哈夫曼树
*
* @param Data 数据序列
* @param Length 数据序列的长度
* @return HuffmanTree 指向哈夫曼树的指针
*/
HuffmanTree CreateHuffmanTree(ElementType Data[], int Length)
{
int i = 0;
HuffmanTree T = NULL;
MinHeap H = CreateMinHeap(Length);
for (int i = 0; i < Length; i++)
{
T = (TreeNode *)malloc(sizeof(TreeNode)); // 创建新结点
T->Weight = Data[i];
T->Left = T->Right = NULL;
Insert(H, T); // 将元素加入到最小堆
}
for (int i = 1; i < Length; i++) // 做Length-1次合并
{
T = (HuffmanTree)malloc(sizeof(TreeNode)); // 创建新结点
T->Left = Delete(H); // 从最小堆中删除一个结点,作为新T的左子节点
T->Right = Delete(H); // 从最小堆中删除一个结点,作为新T的右子节点
T->Weight = T->Left->Weight + T->Right->Weight; // 新T的权值为左右子节点的权值之和
Insert(H, T);
}
T = Delete(H);
return T;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报