哈夫曼树和哈夫曼编码

基础概念

 哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。

结点的权值:
将树中结点赋给一个含有某种含义的数值。记为:Wi(i=1,2,...n)
路径长度:
等于路径上的结点数减1。
结点的带权路径长度:
从根结点到该结点的路径长度与该结点的权值乘积。记为:Li(i=1,2,...n)。

树的带权路径长度:
树中所有叶子结点的带权路径长度之和
记为WPL
= (W1L1+W2L2+W3L3+...+WnLn),

如果让这N个结点,构成一棵有N个叶结点的二叉树,可以证明哈夫曼树的WPL是最小的。

哈夫曼树也是效率最高的判别树。( 判断树:用于描述分类的判断树)
注意:

  1. 满二叉树不一定是哈夫曼树;
  2. 哈夫曼树中权值越大的叶子离根越近
  3. 具有相同带权结点的哈夫曼树不惟一

构造思想

构造哈夫曼口诀:

  1. 构造森林全是根,
  2. 先用两小造新树,
  3. 删除两小添新人,
  4. 重复2,3剩单根

具体来说就是:

  1. 根据给定的n个权值{w1,w2,…,wn}构成二叉树集合F={T1,T2,…,Tn},其中每棵二叉树Ti中只有一个带权为wi的根结点,其左右子树为空.
  2. 在F中选取两棵根结点权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为左右子树根结点的权值之和.
  3. 在F中删除这两棵树,同时将新的二叉树加入森林中.
  4. 重复2、3,直到F只含有一棵树为止.(得到哈夫曼树)
    image

构造代码

伪代码:

将n个结点放入集合S
while (S中的结点数 > 1){
取走S中2个权值最小的结点,计算它们值之和,并加入S
}
return 集合S剩下的结点 // 剩下的结点就是根结点

借助优先队列实现代码:

#include <bits/stdc++.h>
using namespace std;

struct Huffman
{
    Huffman *lchild;
    Huffman *rchild;
    double data;
};

struct cmp
{ /* 规定优先队列的排序规则 */
    bool operator()(Huffman *a, Huffman *b) const
    {
        return (a->data > b->data); /* 小顶堆 */
    }
};

int main()
{
    priority_queue<Huffman *, vector<Huffman *>, cmp> Q;

    double x;
    while (cin >> x)
    {
        Huffman *h = new Huffman();
        h->data = x;
        h->lchild = h->rchild = 0;
        Q.push(h);
    }

    Huffman *p;
    while (Q.size() > 1)
    { /* 如果Q剩余结点大于1,则执行循环体 */
        p = new Huffman();
        Huffman *p1, *p2;
        p1 = Q.top(); /* 依次取出权值最小的两个结点,放入p1和p2 */
        Q.pop();
        p2 = Q.top();
        Q.pop();

        /*
        计算两结点的权值之和,存入p,且因为
        p1->data <= p2->data,可以将权值小
        的结点存入p的左子树,权值大的结点存入p
        的右子树,最后将p放入Q中
        */
        p->data = p1->data + p2->data;
        p->lchild = p1;
        p->rchild = p2;
        Q.push(p);
    }
     Pre(F);
    return 0;
}

Huffman 编码

编码规则:
从根节点到每一个叶子节点的路径上,左分支记为0,右分支记为1, 将这些0与1连起来即为叶子节点的哈夫曼编码。如下图:
image

哈夫曼编码是最优前缀码
前缀编码:
要求任一字符的编码都不能是另一字符编码的前缀。
为什么哈夫曼编码是最优前缀码?

image

代码:

/*
code 变量的初始值是空字符串,采用二叉树后续遍历,如果是非叶子结点,
存在左子树就将code加"0",存在右子树就将code加"1",继续递归调用;
如果是叶子结点,就记录到键值对(map)里。
*/
void huffman_code(Huffman* h, string code, map<double, string>& map_huff)
{
    if (!h)
        return;
    if (h->lchild)
        huffman_code(h->lchild, code + "0", map_huff);
    if (h->rchild)
        huffman_code(h->rchild, code + "1", map_huff);
    if (h->lchild == 0 && h->rchild == 0) {
        map_huff[h->data] = code;
        return;
    }
}


实战

7-3 修理牧场 (25 分)

posted @ 2022-01-25 14:48  kingwzun  阅读(228)  评论(0编辑  收藏  举报