数据结构 哈夫曼树

1、概念

哈夫曼树又称最优树,是一类带权路径长短最短的树。

“带权路径长度最短”是在“度相同”的树中比较而得的结果,因此有最优二叉树、最优三叉树之称等等。
 

路径:从树中一个结点到另一个结点之间的分支构成这两个结点间的路径。
结点的路径长度:两结点间路径上的分支数。
树的路径长度:从树根到每一个结点的路径长度之和。
结点数目相同的二叉树中,完全二叉树是路径长度最短的二叉树。


权(weight):将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。
结点的带权路径长度:从根结点到该结点之间的路径长度与该结点的权的乘积。
树的带权路径长度:树中所有叶子结点的带权路径长度之和。


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


2、构造哈夫曼树

 贪心算法:构造哈夫曼树时首先选择权值小的叶子结点

 哈夫曼树的结点的度数为0或2,没有度为1的结点。

包含n个叶子结点的哈夫曼树中共有2n-1个结点。
1、在哈夫曼算法中,初始时有n棵二叉树,要经过n-1次合并最终形成哈夫曼树。
2、经过n-1次合并产生n-1个新结点,且这n-1个新结点都是具有两个孩子的分支结点。
3、可见:哈夫曼树中共有n+n-1 = 2n-1个结点,且其所有的分支结点的度均不为1。


3、哈夫曼树创建实现

 1.初始化HT [1....2n-1]: lch=rch=parent=0;

2.输入初始n个叶子结点:置HT[1...…n]的weight值;

3.进行以下n-1次合并,依次产生n-1个结点HT[i], i=n+1.....2n-1:
a)在HT[1..i-1]中选两个未被选过(从parent == 0的结点中选)的weight最小的两个结点HT[s1]和HT[s2], s1、s2为两个最小结点下标;
b)修改HT[s1]和HT[s2]的parent值: HT[s1] .parent=i; HT[s2] .parent=i;
c)修改新产生的HT[i]

HT[i].weight=HT[s1].weight + HT[s2].weight;

HT[i]. lch=s1; HT[i]. rch=s2;
 

typedef struct
{
    int weight;
    int parent, lch, rch;
} HTNode, *HuffmanTree;

void CreatHuffmanTree(HuffmanTree HT, int n)
{ // 构造哈夫曼树——哈夫曼算法
    if (n <= 1)
        return;
    m = 2 * n - 1;          // 数组共2n-1个元素
    HT = new HTNode[m + 1]; // Q号单元未用,HT[m]表示根结点
    for (i = 1; i <= m; ++i)
    { // 将2n-1个元素的lch、rch、parent置为0
        HT[i].lch = 0;
        HT[i].rch = 0;
        HT[i].parent = 0;
    }
    for (i = 1; i <= n; ++i)
        cin >> HT[i].weight; // 输入前n个元素的weight值
//初始化结束,下面开始建立哈夫曼树
    for (i = n + 1; i <= m; i++)
    {                              // 合并产生n-1个结点——构造Huffman树
        Select(HT, i - 1, s1, s2); // 在HT[k](1≤k≤i-1)中选择两个其双亲域为0,
        // 且权值最小的结点,并返回它们在HT中的序号s1和s2
        HT[s1].parent = i;
        HT[s2].parent = i; // 表示从F中删除s1,s2
        HT[i].lch = s1;
        HT[i].rch = s2;                               // s1,s2分别作为i的左右孩子
        HT[i].weight = HT[s1].weight + HT[s2].weight; // i的权值为左右孩子权值之和}
    }
}

4、哈夫曼编码

在通讯领域,经常需要将需要传送的文字转换成由二进制字符组成的字符串。在实际应用中,由于总是希望被传送的内容总长尽可能的短,如果对每个字符设计长度不等的编码,且让内容中出现次数较多的字符采用尽可能短的编码,则整个内容的总长便可以减少。另外,需要保证任何一个字符的编码都不是另一个字符的编码前缀,这种编码成为前缀编码。

而哈夫曼编码就是一种二进制前缀编码,是最优前缀码

哈夫曼编码实现:

1、统计字符集中每个字符在电文中出现的平均概率。概率越大,要求编码越短。
2、利用哈夫曼树的特点:权越大的叶子离根越近;将每个字符的概率值作为权值,构造哈夫曼树。则概率越大的结点,路径越短。
3、在哈夫曼树的每个分支上标上0或1:结点的左分支标0,右分支标1。把从根到每个叶子的路径上的标号连接起来,作为该叶子代表的字符的编码。

void CreatHuffmanCode(HuffmanTree HT, HuffmanCode &HC, int n)
{ // 从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中
    HC = new char *[n + 1];
    // 分配n个字符编码的头指针矢量
    cd = new char[n];
    // 分配临时存放编码的动态数组空间
    cd[n - 1] = '\0';
    // 编码结束符
    for (i = 1; i <= n; ++i)
    {
        // 逐个字符求哈夫曼编码
        start = n - 1;
        c = i;
        f = HT[i].parent;
        while (f != O)
        {
            // 从叶子结点开始向上回溯,直到根结点
            -- start;
            // 回溯一次start向前指一个位置
            if (HT[f].lchild = = c)
                cd[start] = 'O'; //结点c是f的左孩子,则生成代码0
                else cd[start] = '1';
            // 结点c是f的右孩子,则生成代码1
            c = f;
            f = HT[f].parent;
            // 继续向上回溯
        }
        // 求出第i个字符的编码
        HC[i] = new char[n - start];
        // 为第i个字符串编码分配空间
        strcpy(HC[i], &cd[start]); // 将求得的编码从临时空间cd复制到HC的当前行中}
        delete cd;
        // 释放临时空间
    }
    }//  CreatHuffanCode

哈夫曼树是一种特殊的二叉树结构,用于编码和解码数据。在哈夫曼树中,每个叶子节点都代表一个字符或符号,并且具有一个与之关联的权值,代表该字符或符号出现的频率或概率。根据哈夫曼树的概念,我们可以通过给定的叶子节点的权值来构建哈夫曼树。 对于给定的叶子节点的权值,构建哈夫曼树的步骤如下: 1. 首先,根据叶子节点的权值从小到大进行排序。 2. 选取权值最小的两个叶子节点,并将它们作为两个子节点创建一个新的父节点。新父节点的权值等于这两个子节点的权值之和。 3. 将这个新的父节点插入到叶子节点中,同时删除原来的两个子节点。 4. 重复步骤2和步骤3,直到只剩下一个节点,即根节点,这个节点就是哈夫曼树的根节点。 根据题目提供的例子,我们可以看到一种不是建树的方法,只使用数组来模拟哈夫曼树构造过程。这种方法是通过数组来存储节点的信息,并通过一些特定的计算方式来模拟构建哈夫曼树的过程。 根据题目的描述,我们需要根据叶子节点的个数和权值来生成哈夫曼树,并计算所有节点的值与权值的乘积之和。这个问题可以通过构建哈夫曼树的步骤来解决。首先,我们需要将叶子节点根据权值进行排序。然后,按照步骤2和步骤3构建哈夫曼树,直到只剩下一个节点。最后,计算所有节点的值与权值的乘积之和。 综上所述,数据结构哈夫曼树的例题是通过给定叶子节点的权值来构建哈夫曼树,并计算所有节点的值与权值的乘积之和。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [超好理解的哈夫曼树(最优二叉树)与例题](https://blog.csdn.net/weixin_45720782/article/details/109316157)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值