数据结构—哈夫曼树

158 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情

前言

了解了树、二叉树、森林之后,我们还需要了解一种最优二叉树——哈夫曼树,有时候也叫赫夫曼树。一下皆称哈夫曼树。哈夫曼树作为最优二叉树,有很大的用处,下面我们来了解一下。

术语

在学习哈夫曼树之前,我们需要了解一些术语,对我们深入理解哈夫曼树有很大帮助。

  1. 路径:由一个结点到另一结点间的分支所构成
  2. 路径长度:路径上的分支数目
  3. 树的路径长度:从根到每一结点的路径长度之和
  4. 带权路径长度:结点到根的路径长度与结点上权值的乘积
  5. 树的带权路径长度:树中所有叶子结点的带权路径长度之和
  6. 哈夫曼树:带权路径长度最小的树

相信通过这循序渐进的术语,大家已经理解了哈夫曼树,简单来说就是带权路径长度最小的树

构造哈夫曼树

构造哈夫曼树的步骤是:

  1. 根据给定的n个权值{ω1,ω2,……,ωn },构成n棵二叉树的 集合F={T1,T2,……,Tn },其中每棵二叉树Ti中只有一个带权为 ωi的根结点,其左右子树均空。
  2. 在F中选取两棵根结点的权值最小的树作为左右子树构造一棵 新的二叉树,且新的二叉树的根结点的权值为其左、右子树上根结 点的权值之和
  3. 在F中删除这两棵树,同时将新得到的二叉树加入F中
  4. 重复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四个权值构造生成的哈夫曼树

image.png

哈夫曼树的特点

  1. 没有度为1的结点
  2. n个叶子结点的哈夫曼树共有2n-1个结点
  3. 哈夫曼树的任意非叶节点的左右子树交换后仍是哈夫曼树;

哈夫曼编码

哈夫曼树的主要应用就是哈夫曼编码

想象有电文”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

image.png