数据结构应用实例(三)——赫夫曼编码
Content:
- 一、问题描述
- 二、算法思想
- 三、代码实现
- 四、小结
一、问题描述
对一篇英文文章,统计各字符(仅限于26个小写字母)出现的次数,并据此进行 Huffman 编码。
二、算法思想
首先,打开文本文件,统计各个小写英文字母出现的次数;
然后,统计出现过的字母个数 num;
如果 num<=1,进行相关信息的显示,不进行 Huffman 编码;
否则,进行 Huffman 编码:
准备工作:存储出现过的字母编号及出现次数;
1、根据出现次数,创建 Huffman 树:
(1)根据求得的 num 个出现次数 { w 1 , w 2 , ⋯ , w n u m } \lbrace w_1,w_2,\cdots,w_{num} \rbrace {w1,w2,⋯,wnum} 构成 num 棵二叉树的集合 F = { T 1 , T 2 , ⋯ , T n u m } F=\lbrace T_1,T_2,\cdots,T_{num} \rbrace F={T1,T2,⋯,Tnum},其中每棵二叉树 T i T_i Ti 中只有一个带权为 w i w_i wi 的根节点,其左右子树均为空;
(2)在 F 中选取两棵根节点权值最小的树作为左右子树构造一棵新二叉树,其根节点的权值为两棵子树根节点权值之和;
(3)从 F 中删除这两棵树,并将新生成的树添加到 F 中;
(4)重复(2)和(3),直到 F 中只含一棵树。所得到的树就是 Huffman 树;
2、根据 Huffman 树进行 Huffman 编码
约定 ‘0’ 表示左分支,‘1’ 表示右分支。从某个叶子节点开始,如果没有父节点,表示编码结束;如果其有父节点,如果为父节点的左孩子,编码尾部添加 ‘0’,如果为右孩子,尾部添加 ‘1’,然后向根节点移动:当前节点变为其父节点,父节点变为父节点的父节点,进行相同操作。
编码结束之后,对编码进行逆置,得到正序编码;
3、最后显示出现的字母及其出现的次数和 Huffman 编码;
三、代码实现
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#pragma warning(disable:4996)typedef struct myhuffman//赫夫曼树的节点
{int weight;int parent,lchild,rchild;
}HTnode;typedef struct mylist//存放序号和权重
{int index;int weight;
}LI;int *statis();//统计小写英文字母出现的频次,返回存放频次的数组char **huffmanCoding(int *w, int n);//创建huffman编码,w用于存放叶子节点的权重,n表示叶子节点个数void select(HTnode *HT,int k,int *s1,int *s2);//在HT[1,,,k]中选取parent==0且weight最小的两个节点,返回其序号(*s1),(*s2)int main(void)
{int i,j;int *w,num;int **A;//用于存放频次不为0的字母编号和频次char **HC;//获取小写字母出现次数w=statis(); //统计出现过的字母个数num=0;for (i = 1; i <= 26; i++){if (w[i] != 0)num++;}printf("\n字母出现的次数和Huffman编码如下:\n");if (num == 0)printf("\n没有出现小写英文字母,无需进行Huffman编码\n");else // num>=1{A=(int **)malloc(2*sizeof(int *));A[0]=(int *)malloc((num+1)*sizeof(int));A[1]=(int *)malloc((num+1)*sizeof(int));//向A中存放数据j=1;for (i = 1; i <= 26 && j <= num; i++){if (w[i] != 0){A[0][j]=i;A[1][j]=w[i];j++;}}if (num == 1)printf("只出现了一个字母:%c,频次%d,其Huffman编码为1\n", A[0][1] + 'a' - 1, A[1][1]);else // num>=2{//创建huffman编码HC = huffmanCoding(A[1], num);//显示huffman编码 for (i = 1; i <= num; i++)printf("%c,频次:%2d,编码:%s\n", A[0][i] + 'a' - 1, A[1][i], HC[i]);free(HC);}free(A);}free(w); return 0;
}int *statis()//统计小写英文字母出现的频次,返回存放频次的数组
{int i;int *w;FILE *fp;char ch,filename[50];//初始化w=(int *)malloc(27*sizeof(int));for (i = 1; i <= 26; i++)w[i]=0;//打开文件printf("请输入文件名:");scanf("%s",filename);fp=fopen(filename,"r");if (!fp){printf("文件不存在!\n");exit(-1);}//统计小写字母出现次数printf("\n文本信息为:\n");while (!feof(fp)){ch=fgetc(fp);printf("%c",ch);if ('a' <= ch && ch <= 'z')w[ch - 'a' + 1]++;}printf("\n");//关闭文件fclose(fp);return w;
}char **huffmanCoding(int *w, int n)//创建huffman编码,w用于存放叶子节点的权重,n表示叶子节点个数
{int i,j,k,l;HTnode *HT,p;int m;int s1,s2;char **HC;char *cd;//一、创建huffman树m=2*n-1;//总的节点个数HT=(HTnode *)malloc((m+1)*sizeof(HTnode));//HT为结构体数组,用于存放huffman树的节点//初始化for (i = 1; i <= n; i++)//叶子节点{p.weight=w[i];p.parent=0;p.lchild=p.rchild=0;HT[i]=p;}for (; i <= m; i++)//非叶子节点{p.weight=0;p.parent=0;p.lchild=p.rchild=0;HT[i]=p;}//合并子树for (i = n + 1; i <= m; i++){select(HT,i-1,&s1,&s2);//在HT[1,,,i-1]中选取parent==0且weight最小的两个节点,并返回其序号HT[s1].parent=i;HT[s2].parent=i;HT[i].weight=HT[s1].weight+HT[s2].weight;HT[i].lchild=s1;HT[i].rchild=s2;}//二、生成huffman编码HC=(char **)malloc((n+1)*sizeof(char *));cd=(char *)malloc(n*sizeof(char));cd[n-1]='\0';//字符串结束标志for (i = 1; i <= n; i++)//对各个叶子节点进行编码{j=n-1;k=i;l=HT[i].parent;while (l != 0){j--;if(HT[l].lchild == k)cd[j]='0';elsecd[j]='1';//更新循环变量k=l;l=HT[l].parent;}//为第i个编码分配空间并赋值HC[i]=(char *)malloc((n-j)*sizeof(char));strcpy(HC[i],&cd[j]);}free(HT);free(cd);return HC;
}void select(HTnode *HT, int k, int *s1, int *s2)//在HT[1,,,k]中选取parent==0且weight最小的两个节点,返回其序号(*s1),(*s2)
{int i,j;int n,lastindex;LI *T,temp;if(k <= 1)return;T=(LI *)malloc((k+1)*sizeof(LI));j=0;for (i = 1; i <= k; i++)//挑选parent==0的点{if (HT[i].parent == 0){j++;T[j].index=i;T[j].weight=HT[i].weight;}}//对T冒泡排序,寻找weight最小的两个节点n=j;//parent==0的节点个数lastindex=n;while (lastindex > n - 2)//至多进行两趟冒泡排序{i=lastindex;lastindex=0;for (j = 1; j < i; j++){if (T[j].weight < T[j + 1].weight)//交换两个数据,小的放在后面{temp=T[j];T[j]=T[j+1];T[j+1]=temp;lastindex=j;}}}//返回节点序号(*s1)=T[n].index;(*s2)=T[n-1].index;free(T);
}
四、小结
1、 由于 Huffman 树中没有出度为1的点,所以可以根据叶子节点的个数 n 0 n_0 n0 事先确定总的节点个数为 2 n 0 − 1 2n_0-1 2n0−1。
2、 结构体变量之间可以直接赋值;
3、 在寻找 parent==0 且 weight 最小的两个节点时,只需进行至多两次的冒泡排序,大大提高了查找效率和算法性能;
4、 进行 Huffman 编码时,由于是从叶子节点开始向前回溯到根节点,所以编码时从后向前进行,这样就避免了逆置编码;
5、 对于特殊情况:出现的英文字母个数小于等于1,则不进行 Huffman 编码;
相关文章:
数据结构应用实例(三)——赫夫曼编码
Content: 一、问题描述二、算法思想三、代码实现四、小结 一、问题描述 对一篇英文文章,统计各字符(仅限于26个小写字母)出现的次数,并据此进行 Huffman 编码。 二、算法思想 首先,打开文本文件࿰…...
关于Spring Cloud Gateway中 Filters的理解
Spring Cloud Gateway中 Filters的理解 Filters Filters拦截器的作用是,对请求进行处理 可以进行流量染色 ⭐增加请求头 例子 spring:cloud:gateway:routes:- id: add_request_header_routeuri: http://localhost:8123predicates:- Path/api/**filters:- AddR…...
【实践】应用访问Redis突然超时怎么处理?
目录标题 问题描述分析过程查看监控数据系统监控指标JVM监控指标Redis监控指标分析应用异常单机异常规律集群异常规律统计超时的key 初步结论验证结论访问Redis链路slowlogRedis单节点info all定位redis节点定位异常keybigkeystcpdump定位大key影响 经验总结 问题描述 某产品线…...
Spring Cloud Alibaba核心组件Nacos/Seata/Sentinel
文章目录 Spring Cloud Alibaba介绍Spring Cloud 微服务体系Spring Cloud Alibaba 定位 注册配置中心--Nacos服务治理架构注册中心原理 Nacos介绍Nacos 的关键特性1.服务注册和发现2.动态配置服务3.实时健康监控4.动态DNS服务5.易于集成: Nacos入门示例服务注册与发…...
Ubuntu搭建FTP服务器
1. 首先,我们需要安装和配置xinetd,安装的具体命令如下: sudo apt-get install xinetd 2. 新建tftp工作目录,并添加读、写、执行权限(没有权限后面无法正常访问该文件夹),如下图所示。 3. 安装…...
Redis在单线程下删除大Key会发生什么?怎么删除大Key?
大Key的定义 大Key是指在缓存系统(如Redis)或分布式存储中,单个键(Key)对应的数据量非常大,通常存储的是大块数据结构,例如包含大量数据的哈希表、列表、集合或有序集合。这种大Key往往会对系统…...
《Exploit temporal cues in multi-camera 3D object detection》论文泛读
ReadPaperhttps://readpaper.com/pdf-annotate/note?pdfId4666749915775385601eId2491528568128599808 针对单帧数据含有的信息太少的问题,提出了一种新的方法,BEVDet4D,这种方法可以访问时间线索,并且取得了较好的表现ÿ…...
十四、centos7 yum报错:cannot find a valid baseurl for repo:base/7/x86_64的解决方案
🌻🌻目录🌻🌻 一、 centos7 yum报错:cannot find a valid baseurl for repo:base/7/x86_64二、分析错误三、解决方案3.1 检查网络连接3.2 检查DNS设置3.3 检查YUM仓库配置3.3.1 使用官方CentOS镜像配置3.3.2 使用阿里云…...
qt使用对数坐标的例子,qchart用QLogValueAxis坐标不出图解决
硬件:ThinkPad T15 系统:win10 专业版 qt版本:Qt 5.14.1 , QtCreator 4.11.1 软件界面放了一个QPushButton,一个QVBoxLayout,如下: 主要代码如下,我添加了两条曲线,…...
Python 爬虫入门 - 爬虫 requests 请求
在当今互联网时代,数据的获取变得尤为重要,而网络爬虫作为自动化获取数据的一种方式,受到了越来越多编程爱好者和数据分析人员的青睐。Python 语言以其简洁的语法和丰富的库,成为了实现网络爬虫的首选工具。其中,requests库是一个非常流行且强大的工具,用于发送 HTTP 请求…...
flink中startNewChain() 的详解
在 Apache Flink 中,startNewChain() 是一个与算子链(operator chaining)相关的方法。与 disableChaining() 类似,它允许开发者控制算子链的创建方式,但 startNewChain() 的作用是从当前算子开始创建一个新的算子链&am…...
uniapp 苹果安全域适配
一、使用原生占位(仅App端支持) //在manifest.json 文件中 app-plus 中配置 "safearea": { "background": "#FFFFFF", "bottom": { "offset": "auto" } } 二、不使用原生占位 //&…...
linux使用命令行编译qt.cpp
步骤: mkdir qttestcd qttestvim hello.cpp #include <QApplication> #include <QDialog> #include <QLabel> int main(int argc,char* argv[]) {QApplication a(argc,argv);QLabel label("aaa");label.resize(100,100);label.show()…...
Ubuntu 22.04 LTS 上安装 Docker
单台机器安装docker环境,是为了后面安装open-webui,环境安装比较简单,没有难点,但一定要按步骤走,否则还是会遇到一些问题的。 第 1 步:更新软件包并安装必要软件 运行以下命令,更新软件包索引…...
2024秋季云曦开学考
web ezezssrf 打开环境,代码审计 看起来有点多,要绕过五层 第一层:存在弱比较,使用数组或0e绕过 yunxi[]1&wlgf[]2 yunxis878926199a&wlgfs155964671a 第二层:存在强比较,此处使用string限制…...
基于STM32与Qt的自动平衡机器人:从控制到人机交互的的详细设计流程
一、项目概述 目标和用途 本项目旨在开发一款基于 STM32 控制的自动平衡机器人,结合步进电机和陀螺仪传感器,实现对平衡机器人的精确控制。该机器人可以用于教育、科研、娱乐等多个领域,帮助用户了解自动控制、机器人运动学等相关知识。 技…...
C#使用ZipFile的方法CreateFromDirectory
由于现在数据越来越大,虽然磁盘的大小也在增加,但是数据增加的速度是远超过磁盘的增加速度。 因为数据是一种思想的表现,特别是ChatGPT的AI出现,导致很多数据无限地使用机器化地产生,所以数据压缩还是很常有的事情,毕竟压缩之后可以减少磁盘空间的占用。 在C#里有一个专…...
Redis 哨兵模式的选举算法是什么?
Redis 哨兵模式中的选举算法主要用于在主节点出现故障时,从多个 Sentinel 节点中选出一个领导者(Leader)来执行故障转移操作。 Redis 哨兵的选举算法基于 Raft 算法的简化版本,但不完全等同于标准的 Raft 算法。以下是其主要过程: 一、发现主节点故障 当一个 Sentinel …...
Linux shell编程学习笔记80:gzip命令——让文件瘦身
0 引言 在 Linux shell编程学习笔记76:tar命令——快照 & 备份(上)-CSDN博客 Linux shell编程学习笔记77:tar命令——快照 & 备份(下)_linux 系统快照-CSDN博客 Linux shell编程学习笔记78&am…...
【字幕】恋上数据结构与算法之01为什么要学习数据结构与算法
视频地址:请查看01为什么要学习数据结构与算法_哔哩哔哩_bilibili 同志们好,我是小码哥的mj李明杰。非常欢迎大家来学习链上数据结构与算法,从今天开始呢就由我来带大家一起来学习和掌握这个数据结构与算法啊。在正式学习之前我们先来看一下…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
如何配置一个sql server使得其它用户可以通过excel odbc获取数据
要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据,你需要完成以下配置步骤: ✅ 一、在 SQL Server 端配置(服务器设置) 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到:SQL Server 网络配…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...
