C程序实践 哈夫曼(Huffman)树代码

2024-05-24 22:38

本文主要是介绍C程序实践 哈夫曼(Huffman)树代码,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

/*
Slyar
2009.10.29
*/#include <stdio.h>
#include <stdlib.h>
#include <io.h>#define N 255
#define M 2*N - 1/* 运行成功标记 */
int ok = 0;/* 保存原文件名 */
char file[20] = {};/* 哈夫曼树节点类型 */
typedef struct
{int data; /* 字符值 */int weight; /* 权重 */int parent; /* 双亲结点 */int lchild; /* 左孩子结点 */int rchild; /* 右孩子结点 */
}Tree;/* 哈夫曼编码类型 */
typedef struct
{/* 存放哈夫曼码 */char cd[N]; int start;
}Code;/* 生成节点及编码数组 */
Tree ht[M];
Code hcd[N];/* 函数声明 */
int cmp(const void*, const void*);
int NumberOfChar();
void Reset();
void InputFile();
void Encode(int);
void Decode(int);
void OutputFile(int);
void PrintHuffmanCode(int);
void CreateHT(int);
void CreateHCode(int);/* 主函数 */
int main()
{int x = 0;int n = 0;char ch;while(1){system("cls");printf("1. 读入原文件\n");printf("2. 在屏幕上打印哈夫曼代码表\n");printf("3. 编码原文件\n");printf("4. 解码原文件\n");printf("5. 退出\n");printf("Input 1-5:");scanf("%d", &x);switch(x){case 1:Reset();InputFile();if (ok){/* 排序使得有效字符在前面 */qsort(ht, N, sizeof(Tree), cmp);/* 记下有效字符的个数 */n = NumberOfChar();CreateHT(n);CreateHCode(n);OutputFile(n);system("pause");}break;case 2:system("cls");if (ok){PrintHuffmanCode(n);}else{printf("原文件尚未读入!\n");}system("pause");break;case 3:system("cls");if (ok){Encode(n);}else{printf("原文件尚未读入!\n");}system("pause");break;case 4:system("cls");if (ok){Decode(n);}else{printf("原文件尚未读入!\n");}system("pause");break;case 5:return 0;default:/* 防止输入错误序号,刷新缓冲区 */fflush(stdin);}}return 0;
}/* 快速排序比较函数 */
int cmp(const void *a ,const void *b)
{return (*(Tree *)a).weight < (*(Tree *)b).weight ? 1 : -1;
}/* 统计有效的字符数量 */
int NumberOfChar()
{int i, num = 0;for (i = 0; i < N; i++){if (ht[i].weight > 0) num++;}return num;
}/* 初始化哈夫曼树 */
void Reset()
{int i;for (i = 0; i < M; i++){ht[i].weight = 0;ht[i].parent = -1;ht[i].lchild = -1;ht[i].rchild = -1;}
}/* 读入文件内容 */
void InputFile()
{FILE *fp;char ch;system("cls");printf("请输入原文件名:");fflush(stdin);scanf("%s", file);/* 打开原文件 */if ((fp = fopen(file, "rt")) == NULL){printf("找不到原文件 %s!\n", file);ok = 0;system("pause");return;}/* 读入字符并处理权重 */while (fscanf(fp, "%c", &ch) != EOF){ht[ch].data = ch;ht[ch].weight++;}/* 关闭文件指针 */fclose(fp);printf("原文件 %s 读入成功!\n", file);ok = 1;
}/* 编码 */
void Encode(int n)
{int i, k;char ch;FILE *fp1, *fp2;/* 利用哈夫曼代码表进行编码 */if (access("Huffman_Code.txt",0) != 0){printf("找不到代码表 Huffman_Code.txt !\n");return;}/* 打开要编码的原文件 */if ((fp1 = fopen(file, "rt")) == NULL){printf("找不到原文件 %s !\n", file);return;}/* 生成编码文件 */if ((fp2 = fopen("Encode.txt", "wt+")) == NULL){printf("编码文件 Encode.txt 创建失败!\n");return;}/* 一个字符一个字符替换 */while (fscanf(fp1, "%c", &ch) != EOF){for (i = 0; i < n; i++){if (ht[i].data == ch){for (k = hcd[i].start; k <= n; k++){fprintf(fp2, "%c", hcd[i].cd[k]);}}}}/* 关闭文件指针 */fclose(fp1);fclose(fp2);printf("编码成功,结果在 Encode.txt 中!\n");
}/* 解码 */
void Decode(int n)
{int i, k;char ch;FILE *fp1, *fp2;/* 打开要解码的文件 */if ((fp1 = fopen("Encode.txt", "rt")) == NULL){printf("找不到编码文件 Encode.txt!\n");return;}/* 生成解码文件 */if ((fp2 = fopen("Decode.txt", "wt+")) == NULL){printf("解码文件 Decode.txt 创建失败!\n");return;}/* 利用哈夫曼树解码 */i = 2 * n - 2;while (fscanf(fp1, "%c", &ch) != EOF){if (ch == '0'){i = ht[i].lchild;}else{i = ht[i].rchild;}/* 找到叶子节点为止 */if (ht[i].lchild == -1){fprintf(fp2, "%c", ht[i].data);/* 继续从根节点开始查找 */i = 2 * n - 2;}}/* 关闭文件指针 */fclose(fp1);fclose(fp2);printf("解码成功,结果在 Decode.txt 中!\n");
}/* 输出哈弗曼编码到文件 */
void OutputFile(int n)
{FILE *fp;int i, k;/* 生成哈夫曼代码表文件 */if ((fp = fopen("Huffman_Code.txt", "wt+")) == NULL){printf("代码表文件 Huffman_Code.txt 创建失败!\n");return;}/* 将内存里的东西写入文件 */for (i = 0; i < n; i++){fprintf(fp, "%d ", ht[i].data);for (k = hcd[i].start; k <= n; k++){fprintf(fp, "%c", hcd[i].cd[k]);}if (i < n - 1) fprintf(fp, "\n");}/* 关闭文件指针 */fclose(fp);printf("代码表文件 Huffman_Code.txt 生成成功!\n");
}/* 打印哈夫曼编码到屏幕 */
void PrintHuffmanCode(int n)
{int i, k;printf("ASCII \t Char \t HuffmanCode\n");for (i = 0; i < n; i++){printf("%d \t \"%c\" \t ", ht[i].data, ht[i].data);for (k = hcd[i].start; k <= n; k++){printf("%c", hcd[i].cd[k]);}printf("\n");}
}/* 构造哈夫曼树 */
void CreateHT(int n)
{int i, k ,lmin, rmin;int min1, min2;/* 一共有2n-1个节点 */for (i = n; i < 2 * n - 1; i++){/*lmin和rmin为最小权重的两个节点置*/min1 = min2 = 0x7FFFFFFF;lmin = rmin = -1;for (k = 0; k <= i - 1; k++){/*只在尚未构造二叉树的节点中查找*/if (ht[k].parent == -1){if (ht[k].weight < min1){min2 = min1;rmin = lmin;min1 = ht[k].weight;lmin = k;}else if (ht[k].weight < min2){min2 = ht[k].weight;rmin = k;}}}/* 修改2个小权重节点的双亲 */ht[lmin].parent = i;ht[rmin].parent = i;/* 修改双亲的权重 */ht[i].weight = ht[lmin].weight + ht[rmin].weight;/* 修改双亲的孩子 */ht[i].lchild = lmin;ht[i].rchild = rmin;}
}/* 得到哈夫曼编码 */
void CreateHCode(int n)
{int i, f, c;Code hc;/* 根据哈夫曼树求哈夫曼编码 */for (i = 0; i < n; i++){hc.start = n;c = i;f = ht[i].parent;/* 循序直到树根结点 */while (f != -1){/* 处理左孩子结点 */if (ht[f].lchild == c){hc.cd[hc.start--] = '0';}/* 处理右孩子结点 */else{hc.cd[hc.start--] = '1';}c = f;f = ht[f].parent;}/* start指向哈夫曼编码最开始字符 */hc.start++;hcd[i] = hc;}
}


 

功能包括从文件中读取文章,将文章转换为哈夫曼编码,将哈夫曼编码还原,就是那几个基本算法的实现啦。

为了方便查看,这里输出的哈夫曼编码是1byte的,其实真正应该将其变为1bit存储,这样才能达到压缩的目的。

 

这篇关于C程序实践 哈夫曼(Huffman)树代码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/999736

相关文章

C++ move 的作用详解及陷阱最佳实践

《C++move的作用详解及陷阱最佳实践》文章详细介绍了C++中的`std::move`函数的作用,包括为什么需要它、它的本质、典型使用场景、以及一些常见陷阱和最佳实践,感兴趣的朋友跟随小编一起看... 目录C++ move 的作用详解一、一句话总结二、为什么需要 move?C++98/03 的痛点⚡C++

JAVA项目swing转javafx语法规则以及示例代码

《JAVA项目swing转javafx语法规则以及示例代码》:本文主要介绍JAVA项目swing转javafx语法规则以及示例代码的相关资料,文中详细讲解了主类继承、窗口创建、布局管理、控件替换、... 目录最常用的“一行换一行”速查表(直接全局替换)实际转换示例(JFramejs → JavaFX)迁移建

Go异常处理、泛型和文件操作实例代码

《Go异常处理、泛型和文件操作实例代码》Go语言的异常处理机制与传统的面向对象语言(如Java、C#)所使用的try-catch结构有所不同,它采用了自己独特的设计理念和方法,:本文主要介绍Go异... 目录一:异常处理常见的异常处理向上抛中断程序恢复程序二:泛型泛型函数泛型结构体泛型切片泛型 map三:文

MyBatis中的两种参数传递类型详解(示例代码)

《MyBatis中的两种参数传递类型详解(示例代码)》文章介绍了MyBatis中传递多个参数的两种方式,使用Map和使用@Param注解或封装POJO,Map方式适用于动态、不固定的参数,但可读性和安... 目录✅ android方式一:使用Map<String, Object>✅ 方式二:使用@Param

SpringBoot实现图形验证码的示例代码

《SpringBoot实现图形验证码的示例代码》验证码的实现方式有很多,可以由前端实现,也可以由后端进行实现,也有很多的插件和工具包可以使用,在这里,我们使用Hutool提供的小工具实现,本文介绍Sp... 目录项目创建前端代码实现约定前后端交互接口需求分析接口定义Hutool工具实现服务器端代码引入依赖获

利用Python在万圣节实现比心弹窗告白代码

《利用Python在万圣节实现比心弹窗告白代码》:本文主要介绍关于利用Python在万圣节实现比心弹窗告白代码的相关资料,每个弹窗会显示一条温馨提示,程序通过参数方程绘制爱心形状,并使用多线程技术... 目录前言效果预览要点1. 爱心曲线方程2. 显示温馨弹窗函数(详细拆解)2.1 函数定义和延迟机制2.2

Springmvc常用的注解代码示例

《Springmvc常用的注解代码示例》本文介绍了SpringMVC中常用的控制器和请求映射注解,包括@Controller、@RequestMapping等,以及请求参数绑定注解,如@Request... 目录一、控制器与请求映射注解二、请求参数绑定注解三、其他常用注解(扩展)四、注解使用注意事项一、控制

C++简单日志系统实现代码示例

《C++简单日志系统实现代码示例》日志系统是成熟软件中的一个重要组成部分,其记录软件的使用和运行行为,方便事后进行故障分析、数据统计等,:本文主要介绍C++简单日志系统实现的相关资料,文中通过代码... 目录前言Util.hppLevel.hppLogMsg.hppFormat.hppSink.hppBuf

Mysql 驱动程序的程序小结

《Mysql驱动程序的程序小结》MySQL驱动程序是连接应用程序与MySQL数据库的重要组件,根据不同的编程语言和应用场景,MySQL提供了多种驱动程序,下面就来详细的了解一下驱动程序,感兴趣的可以... 目录一、mysql 驱动程序的概念二、常见的 MySQL 驱动程序1. MySQL Connector

VS Code中的Python代码格式化插件示例讲解

《VSCode中的Python代码格式化插件示例讲解》在Java开发过程中,代码的规范性和可读性至关重要,一个团队中如果每个开发者的代码风格各异,会给代码的维护、审查和协作带来极大的困难,这篇文章主... 目录前言如何安装与配置使用建议与技巧如何选择总结前言在 VS Code 中,有几款非常出色的 pyt