哈夫曼编码的应用

数据结构与算法课的一个简单实验,记录一下,以供参考。

文章目录

      • 要求
      • 测试样例
      • 统计字母出现次数
      • 建立哈夫曼树
      • 对字符编码
      • 对原文进行编码
      • 译码

要求

  1. 输入一段100—200字的英文短文,存入一文件a中。
  2. 统计短文出现的字母个数n及每个字母的出现次数。
  3. 以字母出现次数作权值,建Huffman树(n个叶子),给出每个字母的Huffman编码。
  4. 用每个字母编码对原短文进行编码,码文存入文件b中。
  5. 用Huffman树对b中码文进行译码,结果存入文件c中,比较a,c是否一致,以检验编码、译码的正确性。

测试样例

节选自J·阿尔弗瑞德·普鲁弗洛克的情歌,存放在文件a.txt中:

Let us go then, you and I,
When the evening is spread out against the sky
Like a patient etherized upon a table;
Let us go, through certain half-deserted streets,
The muttering retreats
Of restless nights in one-night cheap hotels
And sawdust restaurants with oyster-shells:
Streets that follow like a tedious argument
Of insidious intent
To lead you to an overwhelming question ...
Oh, do not ask, “What is it?”
Let us go and make our visit.

这段诗是游戏赛博朋克2077节制结局奥特对v说的,印象深刻,找到英文原版拿来用了。

统计字母出现次数

这块用哈希表来存比较简单,字母为key,出现的次数为valve,借助stl容器unordered_map实现,从文件中一个字符一个字符读,然后将其存到哈希表中。

代码如下:

void analysis()
{
    ifstream ifs("a.txt", ios::in);
    
    char c;
    while (ifs.get(c))
        m[c]++;

    ifs.close();
}

测试代码如下:

for (auto& kv : m)
	cout << kv.first << '>' << kv.second << '\t';
cout << endl;

结果如下:
img

第一行最后的换行是因为回车符。原文正好12行,换了11次行。

建立哈夫曼树

采用链式存储,先给出节点的定义:

struct treeNode
{
    char data;
    int weight;
    treeNode* parent;
    treeNode* lchild;
    treeNode* rchild;
    
    treeNode(char d = 0, int w = 0, treeNode* p = nullptr, treeNode* l = nullptr, treeNode* r = nullptr)
        : data(d), weight(w), parent(p), lchild(l), rchild(r)
    {}  
};
vector<treeNode*> v;

在给出字符的哈夫曼编码时采取的从叶子到根的方式,所以需要保存父节点指针,同时需要记录所有叶子节点,借助stl容器vector保存。

建树的过程很简单,先建立n个叶子结点,然后创建n-1个非叶子节点,每次从节点堆里取出两个权值最低的节点作为新节点,然后加入节点堆重复这个过程,一共循环n-1次。

每次都需要取权值最小的节点,考虑使用小根堆,借助stl容器priority_queue,由于优先队列中存放的是自定义类型指针,需要自定义比较方式,用仿函数实现:

struct treeNodeCompare  
{  
    bool operator()(treeNode* lhs, treeNode* rhs)  
    {  
        return lhs->weight > rhs->weight;
    }  
};

所以建树的代码就好写了:

void createTree()
{
    priority_queue<treeNode*, vector<treeNode*>, treeNodeCompare> q;
    treeNode *e;
    for (auto& kv : m)
    {
        e = new treeNode(kv.first, kv.second);
        q.push(e);
        v.push_back(e);
    }
    
    treeNode *l, *r, *p;
    for (int i = 0; i < m.size() - 1; i++)
    {
        l = q.top();
        q.pop();
        r = q.top();
        q.pop();
        p = new treeNode(0, l->weight + r->weight, nullptr, l, r);
        l->parent = p;
        r->parent = p;
        q.push(p);
    }
}

对字符编码

对字符进行编码,这里采取从下至上的方式,在左分支给0,右分支给1。

所以代码思路就很简单,遍历先前保存的叶子结点集合,一直向上找parent,如果是parent的left就+0,如果是parent的right就+1,直到走到根节点,这样得到的序列是逆过来的,可以选择转置,这个无所谓。

得到字符的编码之后还需要保存一下,后面对短文进行编码和译码的时候都要用。

用哈希表存的话需要存两份,一份字符为key编码为value,对文章编码的时候用;一份编码为key字符为value,译码的时候用,这份其实也可以不要,后续给出不用这个的译码方式。

代码如下:

unordered_map<char, string> ch_hf;
unordered_map<string, char> hf_ch;
void createHfCode()
{
    for (auto e : v)  
    {  
        string tmp;  
        treeNode* p = e;  
        while (p->parent)
        {  
            if (p->parent->lchild == p)  
                tmp += '0';  
            else  
                tmp += '1';  
            p = p->parent;  
        }  
        reverse(tmp.begin(), tmp.end());  
        ch_hf[e->data] = tmp;
        hf_ch[tmp] = e->data;
    }
}

也可以使用先序遍历,遍历到根节点就存储,然后回溯,这样就不需要父节点了,但是需要保存一下根节点:

void dfs(treeNode* p, string& s)
{
	if (p->lchild == nullptr && p->rchild == nullptr)
	{
		hf_ch[s] = p->data;
		ch_hf[p->data] = s;
		return;
	}
	if (p->lchild)
	{
		s += '0';
		dfs(p->lchild, s);
		s.pop_back();
	}
	if (p->rchild)
	{
		s += '1';
		dfs(p->rchild, s);
		s.pop_back();
	}
}

void createHfCode()
{
    string s;
    dfs(root, s);
}

用下面的代码进行这部分的测试:

for (auto& kv : ch_hf)
    printf("%c->%-15s", kv.first, kv.second.c_str());
cout << endl;

结果如下:
image-20240515223825233

第三行突然换行还是因为换行符。

对原文进行编码

这个就很简单了,还是一个一个字符读取,直接向文件b中写入字符对应的编码。

哈希表还是很方便的,代码如下:

void code()
{
    ifstream ifs("a.txt", ios::in);
    ofstream ofs("b.txt", ios::out);
    
    char c;
    while (ifs.get(c))
        ofs << ch_hf[c];
    
    ifs.close();
    ofs.close();
}

编译运行得到文件b,一长串01序列,全部都在一行不好展示,用下面代码进行测试:

ifstream ifs2("b.txt", ios::in);
while (ifs2.get(c))
    cout << c;
cout << endl;
ifs2.close();

结果如下:
image-20240515223801747

译码

第一种方法,使用多保存的那个哈希表。

还是一个字符一个字符地从文件b读取,读一个就加到临时字符串中,然后从哈希表中查询一下是否有以当前字符串为key的kv对,如果有就将其value值写入到文件c中。代码如下:

void encode()
{
	ifstream ifs("b.txt", ios::in);
	ofstream ofs("c.txt", ios::out);
	string tmp;
	char c;
	while (ifs.get(c))
	{
		tmp += c;
		auto it = hf_ch.find(tmp);
		if (it != hf_ch.end())
		{
			ofs << it->second;
			tmp.clear();
		}
	}
	ifs.close();
	ofs.close();
}

第二种方法,不使用哈希表,直接从树中查询,需要保存根节点。

还是一个字符一个字符读取,如果读到0就走到左子树,如果读到1就走到右子树。当走到叶子节点说明已经读完了一个字符的完整哈夫曼编码,可以进行译码,将叶子节点存的字符写入到文件c中。代码如下:

void encode2()
{
	ifstream ifs("b.txt", ios::in);
	ofstream ofs("c.txt", ios::out);
	char c;
	treeNode* p = root;
	while (ifs.get(c))
	{
		if (c == '0')
			p = p->lchild;
		else
			p = p->rchild;
		if (p->data)
		{
			ofs << p->data;
			p = root;
		}
	}
	ifs.close();
	ofs.close();
}

编译运行得到c.txt:
image-20240515223738291

与原文完全一致。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/630739.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

stm32ADC注入通道使用笔记(以STM32F407 为例)

ADC_JDR1 存放的是第一次转换的数据 ADC_JDR2 存放的是第二次转换的数据 ADC_JDR3 存放的是第三次转换的数据 ADC_JDR4 存放的是第四次转换的数据 1.当 JL 0&#xff08;定序器中有 1 次注入转换&#xff09;时&#xff0c;ADC 将仅转换 JSQ4[4:0] 通道。值存入ADC_JDR1中…

表现层设计模式_1.MVC模式

1.MVC模式三个核心模块 MVC是一种目前广泛流行的软件设计模式。近年来&#xff0c;随着Java EE的成熟&#xff0c;MVC成为了Java EE平台上推荐的一种设计模式。MVC强制性地把一个应用的输入、处理、输出流程按照视图、控制、模型的方式进行分离&#xff0c;形成了控制器…

战网国际服加速器哪个好用 暴雪战网免费加速器分享

战网国际服&#xff08;Battle.net International或Battle.net Global&#xff09;是由暴雪娱乐公司&#xff08;Blizzard Entertainment&#xff09;运营的面向全球玩家的多人在线游戏平台。与专注于特定地区的版本不同&#xff0c;国际服允许玩家不受地域限制地访问暴雪的多款…

1:硬件测试面试

1&#xff1a;板级测试 . JTAG和Boundary Scan 对于硬件板级测试&#xff0c;我使⽤JTAG和Boundary Scan技术进⾏⾃动化测试。这些技术可以帮助我访问PCB 上的芯⽚引脚&#xff0c;从⽽进⾏信号测量、连通性测试和故障诊断。 2&#xff1a;整机测试 3&#xff1a;测试准备 4…

第189题|幂级数的展开的常规方法(二)|武忠祥老师每日一题

解题思路&#xff1a;将函数展开成幂级数有两种方法&#xff1a;一种是直接法&#xff08;这种一般比较麻烦&#xff09;&#xff0c;一种是拆解成现有展开式展开&#xff08;这种的特征一般是能因式分解&#xff09;。 第一步&#xff1a; 这里看到 ln(1-x-2x^2) 将里面的式…

【图神经网络——消息传递】

消息传递机制 画图先&#xff1a;导包&#xff1a;画图&#xff1a; 实现消息传递&#xff1a;例子一&#xff1a;例子二&#xff1a; 画图先&#xff1a; 导包&#xff1a; import networkx as nx import matplotlib.pyplot as plt import torch from torch_geometric.nn im…

蛋白聚乙二醇化修饰检测试剂盒

蛋白多肽因其高生物活性、高特异性等优点备受药物开发商和研究者的青睐。但分子量大、亲水性强、稳定性差等劣势限制了蛋白多肽在临床上的应用&#xff0c;特别是蛋白多肽作为一种异源蛋白具有很强的免疫原性&#xff0c;容易被机体免疫系统识别并清除&#xff0c;导致药物的血…

动态规划-两个数组的dp问题3

文章目录 1. 两个字符串的最小ASCII删除和&#xff08;712&#xff09;2. 最长重复子数组&#xff08;718&#xff09; 1. 两个字符串的最小ASCII删除和&#xff08;712&#xff09; 题目描述&#xff1a; 状态表示&#xff1a; 根据经验以及题目要求&#xff0c;建立二维数…

zabbix触发器配置定期生效教程

在企业生产过程中&#xff0c;并非所有的设备都需要全天候、满负载运转&#xff0c;也有些仅需要周期性的运转即可。例如&#xff0c;在某家企业&#xff0c;有一批这样的机器&#xff0c;每天都会在固定的时间跑批量任务&#xff0c;期间&#xff0c;机器的CPU使用率会有明显的…

学前端网络安全这块还不懂?细说CSRF

什么是CSRF&#xff1f; 举个栗子&#xff0c;比如我们需要在某个博客上删除一个文章&#xff0c;攻击者首先在自己的域构造一个页面&#xff0c;使用了一个img标签&#xff0c;其地址指向了删除博客的链接。攻击者诱使目标用户&#xff0c;也就是博客主访问这个页面&#xff…

脉冲水路清洗机,全自动脉冲技术清除管道堵塞

邦注脉冲水路清洗机是一种高效的清洗设备&#xff0c;它利用全自动脉冲技术来清除管道内的堵塞和污垢。以下是对该设备的一些详细描述&#xff1a; 全自动脉冲技术&#xff1a;脉冲水路清洗机采用了全自动脉冲技术&#xff0c;这是一种先进的清洗方法。该技术通过产生高强度的…

##21 深入理解文本处理:使用PyTorch进行NLP基础操作

文章目录 前言简介文本预处理实现分词构建词汇表 文本向量化构建简单的文本分类模型结论 前言 在现代深度学习应用中&#xff0c;文本处理是不可或缺的一部分&#xff0c;尤其在自然语言处理&#xff08;NLP&#xff09;领域。借助强大的框架如PyTorch&#xff0c;我们可以更加…

AI 绘画神器 Fooocus 图生图:图像放大或变化、图像提示、图像重绘或扩充、反推提示词、生成参数提取、所需模型下载

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里&#xff0c;订阅后可阅读专栏内所有文章。 大家好&#xff0c;我是水滴~~ 本文讲述 Fooocus 的图生图功能&#xff0c;主要内容包括&#xff1a;图像放大或变化、图像提示、图像重绘或扩充、反推…

Typora+PicGo+Gitee设置图床,解决CSDN上传markdown文件图片加载不出来的问题(超级实用)

注&#xff1a; 由于gitee现在已经加上了防盗链&#xff0c;并且只支持1M的图片&#xff0c;我觉得不是很好用&#xff08;可以买腾讯云或阿里云等&#xff09;&#xff0c;之后找到比较好点的图床工具也会持续更新的。&#xff08;sm.ms好像还好&#xff0c;github网速不太稳定…

查询新加 字段不返回数据要看 有没有 AllInfoResultMap 有要再里面加字段

查询新加 字段不返回数据要看 有没有 AllInfoResultMap 有要再里面加字段

容联云零代码平台容犀desk:重新定义坐席工作台

在数智化浪潮的推动下&#xff0c;企业亟待灵活适应市场变化、快速响应客户需求&#xff0c;同时还要控制成本并提升效率&#xff0c;传统的软件开发模式因开发周期长、成本高、更新迭代慢等问题&#xff0c;逐渐难以满足企业灵活多变的业务需求。 容犀Desk&#xff0c;观察到…

山东大学计算机考研数据分析,初复试占比6:4,复试内容不少得花精力准备!

山东大学&#xff08;ShandongUniversity&#xff09;&#xff0c;简称山大&#xff0c;位于中国山东&#xff0c;是中华人民共和国教育部直属的综合性全国重点大学&#xff0c;是国家“211工程”、“985工程”重点建设院校&#xff0c;入选“111计划”、“珠峰计划”、“卓越工…

数据结构——直接插入排序

基本思想 再插入第i个元素时&#xff0c;前面i-1个已经排好序。 排序过程 初始状态&#xff08;假设第一个元素为有序&#xff0c;其余均为无序元素&#xff09; 问题一&#xff1a;如何构建初始的有序序列&#xff1f; 办法 将第一个记录看成是初始有序表&#xff0c;然后…

计算概论学习笔记(2)

感谢北大李戈老师讲解的计算概论。 【道阻且长&#xff0c;行则将至】 很多年没有intensive coding&#xff0c;现在这个系列是coding retake&#xff0c;一点点回忆之前的知识&#xff0c;希望能重回到一线。主要内容包括C,C,Pytorch学术前沿项目学习和实践&#xff0c;预计…

AI大模型日报#0515:Google I/O大会、 Ilya官宣离职、腾讯混元文生图大模型开源

导读&#xff1a;欢迎阅读《AI大模型日报》&#xff0c;内容基于Python爬虫和LLM自动生成。目前采用“文心一言”&#xff08;ERNIE 4.0&#xff09;、“零一万物”&#xff08;Yi-34B&#xff09;生成了今日要点以及每条资讯的摘要。 《AI大模型日报》今日要点&#xff1a;谷歌…