最近完成了数据结构课程设计,被分到的题目是《哈夫曼编码和解码》,现在在这篇博文里分享一下自己的成果。

  我在设计时,在网上参考了很多老师和前辈的算法和代码,向他们表示感谢!他们的成果给了我很多启示和帮助。另外,自己的成品中也还有很多不完善的地方,欢迎批评指正。

课题:哈夫曼编码与解码 C++代码实现

(1)统计某电文中字符出现的频率(假设电文中只含有大小写英文字母,以及逗号和点号);
(2)把字符出现的频率作为权值建立哈夫曼树,进行哈夫曼编码,并输出每个字符的编码结果;
(3)对电文进行哈夫曼编码;
(4)把电文的哈夫曼编码进行译码,输出对应电文的内容。

 #include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <ctype.h>
#define MAX 999999 //一个极大值
#define NUM 10 //存储哈夫曼树每个结点
typedef struct Node {
char ch;
int weight; //权值
int parent;
int lchild,rchild;
}HFNode;
//存储每个字符及其哈夫曼编码
typedef struct {
char ch;
char code[NUM];
}HFCharCode; HFNode HT[*-]; //哈夫曼树结构体
HFCharCode HCD[]; //哈夫曼编码结构体
int LeafNum; //叶子结点数
int NodeNum; //所有结点数
char EnterStr[MAX]; //输入的待编码电文
char EnterCode[MAX]; //输入的待解码密文
char RealStr[MAX]; //密文解码后的电文
int AllWeight[]; //存储所有28个字符的权值 void Statistics();
void CreateHFTree();
void SelectMin(int &min1, int &min2);
void CreateHFCode();
void ReverseStr(char *str);
void EncodeStr();
void DecodeHFCode(); int main() {
printf("****** 哈夫曼编码与解码 ******\n\n");
printf("*** 输入一串字符串 ***\n");
scanf("%s", EnterStr);
getchar();
Statistics();
CreateHFTree();
CreateHFCode();
EncodeStr();
printf("\n*** 输入想解码的内容 ***\n");
scanf("%s", EnterCode);
getchar();
DecodeHFCode();
return ;
} //统计每个字符权值
void Statistics() {
int len = strlen(EnterStr);
for(int i = ; i <= ; i++)
AllWeight[i] = ;
for(int j = ; j <= len - ; j++) {
if(isalpha(EnterStr[j])) {
EnterStr[j] = tolower(EnterStr[j]);
AllWeight[EnterStr[j]-'a']++;
}
else if((int)EnterStr[j] == )
AllWeight[]++;
else if((int)EnterStr[j] == )
AllWeight[]++;
else {
printf("\n输入不符合要求!\n\n");
exit(-);
}
}
int i = , j = ;
for( ; i <= ; i++) {
if(AllWeight[i] != ) {
HT[j].weight = AllWeight[i];
HT[j].ch = i+'a';
j++;
}
}
if(AllWeight[i] != ) {
HT[j].weight = AllWeight[i];
HT[j].ch = ',';
j++;
i++;
}
if(AllWeight[i] != ) {
HT[j].weight = AllWeight[i];
HT[j].ch = '.';
}
printf("\n*** 打印每个字符的权值 ***\n");
int n = ;
for(int i = ; i <= ; i++) {
if(AllWeight[i] != ) {
n++;
if(i <= )
putchar('a'+i);
else if(i == )
printf(",");
else
printf(".");
printf(": %d\n", AllWeight[i]);
}
}
LeafNum = n;
NodeNum = *LeafNum-;
} //构造哈夫曼树
void CreateHFTree() {
int i;
for(i = ; i <= LeafNum-; i++) {
HT[i].parent = -;
HT[i].lchild = -;
HT[i].rchild = -;
HT[i].weight = HT[i].weight;
}
for(; i <= NodeNum-; i++) {
HT[i].parent = -;
HT[i].lchild = -;
HT[i].rchild = -;
HT[i].weight = MAX;
}
int min1, min2;
for(i = LeafNum; i <= NodeNum-; i++) {
SelectMin(min1, min2);
HT[min1].parent = i;
HT[min2].parent = i;
HT[i].lchild = min1;
HT[i].rchild = min2;
HT[i].weight = HT[min1].weight + HT[min2].weight;
}
// printf("\n*** 打印哈夫曼树 ***\n");
// for(int i = 0; i <= NodeNum-1; i++) {
// printf("序号:%d 字符:%c 权值:%d 双亲:%d 左孩:%d 右孩:%d\n", i, HT[i].ch, HT[i].weight, HT[i].parent, HT[i].lchild, HT[i].rchild);
// }
}
//找到两个权值最小的二叉树的序号
void SelectMin(int &min1, int &min2) {
int i = ;
int temp;
int wetmin1, wetmin2;
while(HT[i].parent != -)
i++;
wetmin1 = HT[i].weight;
min1 = i;
i++;
while(HT[i].parent != -)
i++;
wetmin2 = HT[i].weight;
min2 = i;
i++;
if(wetmin1 > wetmin2) {
temp = wetmin2;
wetmin2 = wetmin1;
wetmin1 = temp;
temp = min2;
min2 = min1;
min1 = temp;
}
for(; i <= NodeNum-; i++) {
if(HT[i].weight < wetmin1 && HT[i].parent == -) {
wetmin2 = wetmin1;
wetmin1 = HT[i].weight;
min2 = min1;
min1 = i;
} else if(HT[i].weight < wetmin2 && HT[i].parent == -) {
wetmin2 = HT[i].weight;
min2 = i;
}
}
} //进行哈夫曼编码
void CreateHFCode() {
int i, j, len;
for(i = ; i <= LeafNum-; i++) {
len = ;
j = i;
HCD[i].ch = HT[j].ch;
while(HT[j].parent != -) { //不是根节点
if(HT[HT[j].parent].lchild == j) { //是双亲结点的左孩子
HCD[i].code[len++] = ''+; //加上字符0
}else //是右孩子
HCD[i].code[len++] = ''+; //加上字符1
j = HT[j].parent; //往上遍历
}
HCD[i].code[len] = '\0'; //字符串末尾
ReverseStr(HCD[i].code);
}
printf("\n*** 打印每个字符的编码 ***\n");
for(int i = ; i <= LeafNum-; i++)
printf("%c: %s\n", HT[i].ch, HCD[i].code);
}
//将一个字符串反转
void ReverseStr(char *str) {
int i, j;
char c;
for(i = , j = strlen(str)-; i < j; i++, j--) {
c = str[i];
str[i] = str[j];
str[j] = c;
}
} //哈夫曼编码
void EncodeStr() {
int len = strlen(EnterStr);
printf("\n*** 编码结果 ***\n");
for(int i = ; i <= len-; i++) {
for(int j = ; j <= LeafNum-; j++) {
if(EnterStr[i] == HCD[j].ch)
printf("%s", HCD[j].code);
}
}
printf("\n");
} //哈夫曼解码
void DecodeHFCode() {
int k = NodeNum-; //根结点序号, 开始时一定在最后一个
int len = , i = ;
while(EnterCode[i]) {
if(EnterCode[i] == ''+)
k = HT[k].lchild;
else if(EnterCode[i] == ''+)
k = HT[k].rchild;
else {
printf("\n错误! 密文中仅能含有1和0!\n\n");
exit(-);
}
if(HT[k].lchild == - && HT[k].rchild == -) {
RealStr[len++] = HT[k].ch;
k = NodeNum-;
}
i++;
}
RealStr[len] = '\0';
if(k == NodeNum-) {
printf("\n*** 解码结果 ***\n%s\n\n", RealStr);
exit();
}
printf("\n错误! 部分密文无法解密!\n\n");
exit(-);
}

哈夫曼编码与解码的C++实现:建立哈夫曼树、进行哈夫曼编码与解码的更多相关文章

  1. 【视频编解码·学习笔记】7. 熵编码算法:基础知识 & 哈夫曼编码

    一.熵编码概念: 熵越大越混乱 信息学中的熵: 用于度量消息的平均信息量,和信息的不确定性 越是随机的.前后不相关的信息,其熵越高 信源编码定理: 说明了香农熵越信源符号概率之间的关系 信息的熵为信源 ...

  2. c++实现哈夫曼树,哈夫曼编码,哈夫曼解码(字符串去重,并统计频率)

    #include <iostream> #include <iomanip> #include <string> #include <cstdlib> ...

  3. [数据结构与算法]哈夫曼(Huffman)树与哈夫曼编码

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  4. Java数据结构(十二)—— 霍夫曼树及霍夫曼编码

    霍夫曼树 基本介绍和创建 基本介绍 又称哈夫曼树,赫夫曼树 给定n个权值作为n个叶子节点,构造一棵二叉树,若该树的带权路径长度(wpl)达到最小,称为最优二叉树 霍夫曼树是带权路径长度最短的树,权值较 ...

  5. 哈夫曼(huffman)树和哈夫曼编码

    哈夫曼树 哈夫曼树也叫最优二叉树(哈夫曼树) 问题:什么是哈夫曼树? 例:将学生的百分制成绩转换为五分制成绩:≥90 分: A,80-89分: B,70-79分: C,60-69分: D,<60 ...

  6. 数据结构图文解析之:哈夫曼树与哈夫曼编码详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  7. 哈夫曼(Huffman)树和哈夫曼编码

    一.哈夫曼(Huffman)树和哈夫曼编码 1.哈夫曼树(Huffman)又称最优二叉树,是一类带权路径长度最短的树, 常用于信息检测. 定义: 结点间的路径长度:树中一个结点到另一个结点之间分支数目 ...

  8. Java 树结构实际应用 二(哈夫曼树和哈夫曼编码)

     赫夫曼树 1 基本介绍 1) 给定 n 个权值作为 n 个叶子结点,构造一棵二叉树,若该树的带权路径长度(wpl)达到最小,称这样的二叉树为 最优二叉树,也称为哈夫曼树(Huffman Tree), ...

  9. 【视频编解码·学习笔记】8. 熵编码算法:基本算法列举 & 指数哥伦布编码

    一.H.264中的熵编码基本方法: 熵编码具有消除数据之间统计冗余的功能,在编码端作为最后一道工序,将语法元素写入输出码流 熵解码作为解码过程的第一步,将码流解析出语法元素供后续步骤重建图像使用 在H ...

  10. java实现哈弗曼树和哈夫曼树压缩

    本篇博文将介绍什么是哈夫曼树,并且如何在java语言中构建一棵哈夫曼树,怎么利用哈夫曼树实现对文件的压缩和解压.首先,先来了解下什么哈夫曼树. 一.哈夫曼树 哈夫曼树属于二叉树,即树的结点最多拥有2个 ...

随机推荐

  1. shell脚本配置ssh免密登陆

    通过shell脚本配置免密登陆,分为两个脚本,一个是配置文件config.env,一个是正式脚本sshkey.sh. # config.envexport HOST_USER=(root) expor ...

  2. Solr Principal - 工作原理/机制

    From http://lucene.apache.org/solr/guide/7_1/overview-of-documents-fields-and-schema-design.html The ...

  3. DI 容器实务建议

    整理一些有关使用 DI 容器的一些建议事项,主要的参考数据源是 Jimmy Board 的文章:Container Usage Guidelines. 1.容器设定 避免对同一个组件(DLL)重复扫描 ...

  4. SYN6101型 RS485子钟

    SYN6101型  RS485子钟 产品概述 SYN6101型RS485子钟是由西安同步电子科技有限公司精心设计.自行研发生产的一套以通过RS485总线复接或串行与母钟连接的子钟,接收母钟发送来的时间 ...

  5. 分享Sql Server 2008 r2 数据备份,同步服务器数据(二.本地发布,订阅)

    上一篇文章中写到了数据库的本地备份,这一篇主要分享一下关于不同服务器的数据备份,主要是使用sql server中的本地发布,本地订阅功能,在数据库的读写分离中,也会经常性的用到这个功能. 复制-> ...

  6. mybatis以及预编译如何防止SQL注入

    SQL注入是一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL语句被插入到执行的实体字段中(例如,为了转储数据库内容给攻击者).[摘自] SQL injection - Wikipedia SQL ...

  7. node.js的异步I/O、事件驱动、单线程

    nodejs的特点总共有以下几点 异步I/O(非阻塞I/O) 事件驱动 单线程 擅长I/O密集型,不擅长CPU密集型 高并发 下面是一道很经典的面试题,描述了node的整体运行机制,相信很多人都碰到了 ...

  8. 高效 MacBook 工作环境配置,超实用!

    作者:正鹏 & 隃墨 http://www.xialeizhou.com/?p=71 前言 工欲善其事,必先利其器,工具永远都是用来解决问题的,没必要为了工具而工具,一切工具都是为了能快速准确 ...

  9. 巧妙解决element-ui下拉框选项过多的问题

    1. 场景描述 不知道你有没有这样的经历,下拉框的选项很多,上万个选项甚至更多,这个时候如果全部把数据放到下拉框中渲染出来,浏览器会卡死,体验会特别不好 用人会说element-ui的select有一 ...

  10. python的比较关系运算符和逻辑运算符

    比较运算符 运算符 描述 示例 == 检查两个操作数的值是否相等,如果是则条件变为真. 如a=2,b=2则(a == b) 为 true. != 检查两个操作数的值是否相等,如果值不相等,则条件变为真 ...