持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情
前言
了解了树、二叉树、森林之后,我们还需要了解一种最优二叉树——哈夫曼树,有时候也叫赫夫曼树。一下皆称哈夫曼树。哈夫曼树作为最优二叉树,有很大的用处,下面我们来了解一下。
术语
在学习哈夫曼树之前,我们需要了解一些术语,对我们深入理解哈夫曼树有很大帮助。
- 路径:由一个结点到另一结点间的分支所构成
- 路径长度:路径上的分支数目
- 树的路径长度:从根到每一结点的路径长度之和
- 带权路径长度:结点到根的路径长度与结点上权值的乘积
- 树的带权路径长度:树中所有叶子结点的带权路径长度之和
- 哈夫曼树:带权路径长度最小的树
相信通过这循序渐进的术语,大家已经理解了哈夫曼树,简单来说就是带权路径长度最小的树
构造哈夫曼树
构造哈夫曼树的步骤是:
- 根据给定的n个权值{ω1,ω2,……,ωn },构成n棵二叉树的 集合F={T1,T2,……,Tn },其中每棵二叉树Ti中只有一个带权为 ωi的根结点,其左右子树均空。
- 在F中选取两棵根结点的权值最小的树作为左右子树构造一棵 新的二叉树,且新的二叉树的根结点的权值为其左、右子树上根结 点的权值之和
- 在F中删除这两棵树,同时将新得到的二叉树加入F中
- 重复2,3步直到F只含有一棵树为止
下面给出构建代码:
typedef struct TreeNode *HuffmanTree;
struct TreeNode{
int weight;
HuffmanTree Left, Right;
}
HuffmanTree Huffman ( MinHeap H){
/*假设H->Size个权值已经存在H->Elements []->Weight里*/
int i; HuffmanTree T;
BuildMinHeap (H) ; /*将H->Elements []按权值调整为最小堆*/
for (i=1; i < H->Size; i++) { /*做H->size-1次合并*/
T=malloc ( sizeof ( struct TreeNode) ); /*建立新结点*/
T->Left = DeleteMin (H);/*从最小堆中删除一个结点,作为新的左子结点*/T->Right = DeleteMin (H);
/*从最小堆中删除一个结点,作为新T的右子结点*/
T->Weight = T->Left->Weight+T->Right->Weight;
/*计算新权值*/
Insert(H, T); /*将新T插入最小堆*/
}
T = DeleteMin (H) ;return T;
return T;
}
例:以7,5,2,4四个权值构造生成的哈夫曼树
哈夫曼树的特点
- 没有度为1的结点
- n个叶子结点的哈夫曼树共有2n-1个结点
- 哈夫曼树的任意非叶节点的左右子树交换后仍是哈夫曼树;
哈夫曼编码
哈夫曼树的主要应用就是哈夫曼编码
想象有电文”A B A C C D A“ 若A B C D分别表示为00、01、10、11,则电文为00 01 00 10 10 11 00,总长14位; 若A B C D分别表示为0、00、1、01,则电文为0 00 0 1 1 01 0,总长9位,但译码时产生二意性。
想要找到最简便并不产生二义性的编码,则必须任一个字符的编码都不是另一个字符的编码的前缀,称为前缀编码。哈夫曼树就能很好的解决这个问题。由于赫夫曼树的WPL最小,说明编码所需要的长度最小。所以,这种编码已广泛应用于网络通信中
编码方式:左分支为0,右分支为1