哈夫曼树与哈夫曼编码(优先队列)

简介: 哈夫曼树与哈夫曼编码(优先队列)

题目描述:

哈夫曼树(Huffman Tree)又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的路径长度是从树根到每一结点的路径长度之和,记为WPL=(W1L1+W2L2+W3L3+...+WnLn),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。可以证明哈夫曼树的WPL是最小的。


在数据通信中,需要将传送的文字转换成二进制的字符串,用0,1码的不同排列来表示字符。例如,需传送的报文为“AFTER DATA EAR ARE ART AREA”,这里用到的字符集为“A,E,R,T,F,D”,各字母出现的次数为{8,4,5,3,1,1}。现要求为这些字母设计编码。要区别6个字母,最简单的二进制编码方式是等长编码,固定采用3位二进制,可分别用000、001、010、011、100、101对“A,E,R,T,F,D”进行编码发送,当对方接收报文时再按照三位一分进行译码。显然编码的长度取决报文中不同字符的个数。若报文中可能出现26个不同字符,则固定编码长度为5。然而,传送报文时总是希望总长度尽可能短。在实际应用中,各个字符的出现频度或使用次数是不相同的,如A、B、C的使用频率远远高于X、Y、Z,自然会想到设计编码时,让使用频率高的用短码,使用频率低的用长码,以优化整个报文编码。


为使不等长编码为前缀编码(即要求一个字符的编码不能是另一个字符编码的前缀),可用字符集中的每个字符作为叶子结点生成一棵编码二叉树,为了获得传送报文的最短长度,可将每个字符的出现频率作为字符结点的权值赋予该结点上,显然字使用频率越小权值越小,权值越小叶子就越靠下,于是频率小编码长,频率高编码短,这样就保证了此树的最小带权路径长度效果上就是传送报文的最短长度。因此,求传送报文的最短长度问题转化为求由字符集中的所有字符作为叶子结点,由字符出现频率作为其权值所产生的哈夫曼树的问题。利用哈夫曼树来设计二进制的前缀编码,既满足前缀编码的条件,又保证报文编码总长最短。


本题要求从键盘输入若干电文所用符号及其出现的频率,然后构造哈夫曼树,从而输出哈夫曼编码。


注意:

为了保证得到唯一的哈夫曼树,本题规定在构造哈夫曼树时,左孩子结点权值不大于右孩子结点权值。如权值相等,则先选优先级队列中先出队的节点作为左孩子。编码时,左分支取“0”,右分支取“1”。


输入格式:

输入有3行。

第1行:符号个数n(2~20)。


第2行:一个不含空格的字符串。记录着本题的符号表。我们约定符号都是单个的小写英文字母,且从字符‘a’开始顺序出现。也就是说,如果 n 为 2 ,则符号表为 ab ;如果 n 为 6,则符号为 abcdef;以此类推。


第3行:各符号出现频率(用乘以100后的整数),用空格分隔。


输出格式:

先输出构造的哈夫曼树带权路径长度。

接下来输出n行,每行是一个字符和该字符对应的哈夫曼编码。字符按字典顺序输出。


字符和哈夫曼编码之间以冒号分隔。

例如:

a:10

b:110


输入样例:

在这里给出一组输入。

1. 6
2. abcdef
3. 15 19 10 6 38 12


输出样例:

在这里给出相应的输出。

1. 240
2. a:101
3. b:111
4. c:1101
5. d:1100
6. e:0
7. f:100


提示:

以上示例数据,按题目要求建立的Huffman Tree如下图:

98dd5464ea74062f8a749d88b3d640e2.png

代码长度限制        16 KB

时间限制        400 ms

内存限制        64 MB


解析:做这题的时候改了挺久的,总体思路就是根据优先队列和结构体模拟哈弗曼树的过程,题目整体思想不太难,但是有两大坑点:

1:重载运算符的时候需要先对字符进行排序,然后再对权重进行排序

2:当左右孩子一样大的时候,先放右孩子再放左孩子(这点特别坑,题目并没有明确说明,按照正常的思路都是先左后右)

#include <iostream>
#include <queue>
#include <map>
using namespace std;
const int N = 110;
struct node
{
    int id, w;
    char op;
    bool operator < (const node &a) const
    {
        if (a.w == w) return op < a.op;
        return w > a.w;
    }
};
struct node1
{
    int id, w;
    char op;
    int l, r, p;
}h[N];
int n;
string s;
map<char, string>mp;
void init()
{
    for (int i = 0; i < n - 1; i ++ )
        h[i].p = h[i].l = h[i].r = -1;
}
int main()
{
    init();
    cin >> n >> s;
    priority_queue<node, vector<node> >q;
    for (int i = 0; i < n; i ++ )
    {
        int x;
        cin >> x;
        q.push({i, x, s[i]});
    }
    int sum = 0;
    for (int i = n; i < 2 * n - 1; i ++ )
    {
        auto x = q.top();
        q.pop();
        auto y = q.top();
        q.pop();
        if(x.w == y.w) swap(x, y);
        h[i].l = x.id, h[i].r = y.id;
        h[x.id].p = h[y.id].p = i;
        h[i].id = i;
        h[i].w = x.w + y.w;
        q.push({i, h[i].w, '-'});
        sum += h[i].w;
    }
    cout << sum << endl;
    for (int i = 0; i < n; i ++ )
    {
        h[n * 2 - 2].p = -1;
        string s1 = "";
        int pre = i;
        int pp = h[i].p;
        while (pp != -1)
        {
            int ll = h[pp].l;
            int rr = h[pp].r;
            if(ll == pre) s1 = "0" + s1;
            else s1 = "1" + s1;
            pre = pp;
            pp = h[pp].p;
        }
        mp[s[i]] = s1;
    }
    for (auto it : mp)
        cout << it.first << ':' << it.second << endl;
    return 0;
}


目录
相关文章
|
编解码 前端开发 算法
基于OpenCV的双目摄像头测距(误差小)
首先进行双目摄像头定标,获取双目摄像头内部的参数后,进行测距;本文的双目视觉测距是基于BM算法。注意:双目定标的效果会影响测距的精准度,建议大家在做双目定标时,做好一些(尽量让误差小)。
12217 3
基于OpenCV的双目摄像头测距(误差小)
|
11月前
|
人工智能 前端开发 API
OpenAI 12天发布会内容全纪录!一文快速回顾获知亮点信息,原文附发布会中文字幕视频
OpenAI 于12月5日宣布将举行为期12天的系列发布活动,期间每天发布一个产品或样品,包括备受期待的AI视频生成工具Sora和新的推理模型。本文将介绍这12天的发布会每日的发布内容和相关亮点信息。
751 82
|
资源调度 JavaScript 前端开发
总结vue3中常用的组件间通信的方法
总结vue3中常用的组件间通信的方法
168 0
|
编解码 监控 安全
GB/T28181规范扫盲和使用场景探讨
GB28181(GB/T 28181-2022)是中国国家标准,规定了安全防范视频监控联网系统的信息传输、交换、控制技术要求。此标准支持设备接入、音视频传输及控制指令交互等功能,适用于各类监控设备如执法记录仪和移动监控系统。技术实现涉及协议栈构建、音视频编码及数据传输等环节。广泛应用在执法记录、移动监控和铁路巡检等领域。例如,海康威视iSecure Center和萤石云平台均支持GB28181协议,实现设备管理和视频传输。此外,大牛直播SDK推出的SmartGBD为Android终端提供了便捷的GB28181接入解决方案,支持多种数据类型接入,增强了设备的互操作性。
1361 0
|
存储 NoSQL 容灾
数据库非功能需求分析
本文探讨了业务研发在技术设计中如何满足非功能需求,重点关注数据库系统的角色。内容涵盖数据库的可用性、可靠性、性能、可修改性、安全性及成本。文章强调了根据业务场景选择合适的数据类型(如关系型、非关系型、内存型、图数据库和时间序列数据库)以及考虑数据容量和增长速度。对于性能需求,讨论了响应时间、吞吐量和并发处理能力。此外,还提到了升级路径、兼容性、备份方案和成本控制(硬件、软件和人力成本)在数据库管理中的重要性。
509 0
|
缓存 前端开发 算法
前端需要加载一个大体积的文件时,可以这么优化
前端需要加载一个大体积的文件时,可以这么优化
|
Java 数据安全/隐私保护
随机密码生成工具类(java)
随机密码生成工具类(java)
315 0
|
缓存 算法
深入理解操作系统内存管理:分页系统的优势与挑战
【5月更文挑战第28天】 在现代操作系统中,内存管理是一项至关重要的功能,它不仅确保了系统的稳定运行,还提升了资源的利用效率。本文将探讨分页系统这一核心概念,并分析其在内存管理中的优势和面临的挑战。通过剖析分页机制的工作原理及其对虚拟内存实现的重要性,我们进一步阐述了它在多任务处理和内存保护方面的作用。同时,文章也将讨论分页带来的性能开销、页面置换算法的设计以及它们如何影响系统的整体性能。
|
移动开发 缓存 前端开发
H5画布 canvas(三)canvas 库 Konva.js 的使用
H5画布 canvas(三)canvas 库 Konva.js 的使用
1864 0
H5画布 canvas(三)canvas 库 Konva.js 的使用
|
存储 JavaScript 前端开发
跨标签页通信的8种方式(下)
跨标签页通信是指在浏览器中的不同标签页之间进行数据传递和通信的过程。在传统的Web开发中,每个标签页都是相互独立的,无法直接共享数据。然而,有时候我们需要在不同的标签页之间进行数据共享或者实现一些协同操作,这就需要使用跨标签页通信来实现。
320 0