数据结构应用实例(三)——赫夫曼编码
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李明杰。非常欢迎大家来学习链上数据结构与算法,从今天开始呢就由我来带大家一起来学习和掌握这个数据结构与算法啊。在正式学习之前我们先来看一下…...

龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...

使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...