C语言哈夫曼树压缩/解压器

小编是大一的菜鸡,这个题目是数据结构的一个实验题,为了完成这个作业,查找了各种资料,借鉴了很多人的代码,前后折腾了三天左右。代码可能跟网上的不一样,大佬路过请不要踩我。

温馨提醒

建议先认真学习Huffman树、文件操作、位或与等相关知识。代码小编已经加了很多标注,如果有不清楚的地方欢迎交流~

原理

建立Huffman树,可以获得权值总和最小的二叉树,对叶子结点用0或1进行二进制编码,由于一个字节的二进制含有8位,将字符转换为二进制进行存储,可以大大减小存储空间。

头文件

#include<stdio.h>
#include<stdlib.h>
#include<string>//需用到strcpy()
#define MAX_SIZE 100//文件名长度
#define ERROR -1
#define OK 1``

哈夫曼树存储结构

typedef struct {unsigned int weight;//字符权重unsigned int parent, lchild, rchild;
}HTNode,*HuffmanTree;//动态分配数组存储哈夫曼树
typedef char** HuffmanCode;//动态分配数组存储哈夫曼编码表

哈夫曼树相关函数

这些函数基本上都是在严蔚敏的《数据结构》上抄过来的

void HuffmanCoding(HuffmanTree& HT, HuffmanCode& HC, unsigned int* w, int n) {//这里的w为无符号类型Create_HuffmanTree(HT, w, n);Create_HT_Codelist(HT, HC, n);
}
void Create_HuffmanTree(HuffmanTree& HT, unsigned int* w, int n) {if (n <= 1) return;int m = 2 * n - 1;//结点数HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode));//多出一个未用的0号单元if (!HT) exit(ERROR);HTNode* p=HT+1;//跳过0号单元int i = 1;for (; i <= n; i++, p++)*p = { w[i-1],0,0,0 };//n个结点初始化,课本是用*w,最后w++,这里是为防止w[0]地址的丢失for (; i <= m; ++i, ++p) *p = { 0,0,0,0 };//大于n的结点初始化for (i = n + 1; i <= m; ++i) {//建立哈夫曼树//在HT[i..i-1]选择parent为0且weight最小的两个结点,其序号分别为s1和s2int s1, s2;Select(HT, i - 1, s1, s2);HT[s1].parent = i; HT[s2].parent = i;HT[i].lchild = s1; HT[i].rchild = s2;//对左右子树的权值大小没有要求HT[i].weight = HT[s1].weight + HT[s2].weight;}
}
void Create_HT_Codelist(HuffmanTree HT, HuffmanCode& HC, int n) {HC = (HuffmanCode)malloc((n + 1) * sizeof(char*));//分配n个字符编码的头指针char* cd = (char*)malloc(n * sizeof(char));//分配求编码的工作空间if ((!cd)||(!HC)) exit(ERROR);cd[n - 1] = '\0';//编码最多有n-1位,最后一位定义位编码结束符int start;//编码结束符位置int c;for (int i = 1; i <= n; i++) {//逐个字符求哈夫曼编码start = n - 1;c = i;int f = HT[i].parent;for (; f != 0; c = f, f = HT[f].parent) //从叶子到根逆向求编码if (HT[f].lchild == c)cd[--start] = '0';else cd[--start] = '1';//左0右1HC[i] = (char*)malloc((n - start) * sizeof(char));//第0个不用,为第i个字符编码分配空间,'\0'也被存入if (!HC[i]) exit(ERROR);strcpy_s(HC[i],n-start, &cd[start]);//从cd复制编码(串)到HC}free(cd);
}//Creat_HT_Codelist

Select函数是自己写的

void Select(HuffmanTree T, int n, int&s1, int&s2) {if (n <= 1) exit(ERROR);unsigned int m1 = 4294967295; unsigned int m2 = 4294967295;//最大值for(int i=1;i<=n;i++)if ((T[i].weight < m1)&&(T[i].parent==0)) {m1 = T[i].weight;s1 = i;}for (int i = 1; i <= n; i++) if ((T[i].weight < m2) && (i != s1) && (T[i].parent == 0)) {//要确保s1跟s2不相等,结果为s1对应元素的权值<=s2m2 = T[i].weight;s2 = i;}
}

压缩函数

void Compress(char in_file_name[], char out_file_name[]) {FILE* f1,*f2;//f1为待压缩文件指针,f2为压缩后文件指针fopen_s(&f1, in_file_name, "rb");if (!f1)exit(ERROR);// fseek(f1, 0L, SEEK_SET);fseek(f1, 0L, SEEK_END);//将文件指针移到末尾unsigned long long FileSize = ftell(f1);//记录文件总字节数fseek(f1, 0L, SEEK_SET);//将指针移到开头unsigned int *FullWeight=(unsigned int *)malloc(256*sizeof(unsigned int));//记录256个字符是否存在及权值for (int i = 0; i < 256; i++)FullWeight[i] = 0;//初始化for (unsigned long long i = 0; i < FileSize; i++)//将文件的全部字符一个个读取出来FullWeight[(unsigned char)fgetc(f1)]++;//char转换为unsignedfclose(f1);int count=0;//字符种数for (int i = 0; i < 256; i++) if (FullWeight[i]) count++;unsigned char* CharSet = (unsigned char*)malloc(count * sizeof(unsigned char));//记录文件含有的字符unsigned int* WeightSet = (unsigned int*)malloc(count * sizeof(unsigned int));//记录字符的权值if (!CharSet || !WeightSet)exit(ERROR);int j=0;//最后的结果j=count-1for (int i = 0; i < 256; i++) if (FullWeight[i]) {CharSet[j] = i;//字符表种字母顺序为26个英文字母的顺序WeightSet[j] = FullWeight[i];j++;}free(FullWeight);HuffmanTree HT;HuffmanCode HC;HuffmanCoding(HT, HC, WeightSet, count);fopen_s(&f2, out_file_name, "wb");//以二进制的形式写入if(!f2)exit(ERROR);fwrite(&count, sizeof(int), 1, f2);//写入字符个数//fwrite(&FileSize, sizeof(unsigned long long), 1, f2);//写入原文件字节总数//2n个哈夫曼树元素写入fwrite(HT, sizeof(HTNode), 2*count, f2);fwrite(CharSet, sizeof(unsigned char), count, f2);//写入字符表fwrite(WeightSet, sizeof(unsigned int), count, f2);//写入权值表//以下开始编码int offset = 8;//龙哥这里为7,记录一个字节内剩余位数int a = 0;//字符在CharSet里的位置unsigned char ReadByte;//读取的一个字节unsigned char TempByte=0;//暂时存储要写入的编码unsigned long long BitSize = 0;//记录位数,方便进行解压fopen_s(&f1, in_file_name, "rb");//以二进制的形式写入if (!f1)exit(ERROR);for (unsigned long long i = 0; i < FileSize; i++) {fread(&ReadByte, sizeof(unsigned char), 1, f1);a = 0;for (;; a++) if (CharSet[a] == ReadByte) break;for (int b = 0; HC[a+1][b]; b++) {//若读到'\0',则跳出//第一个没用,为HC[a+1][b]TempByte = (TempByte << 1) | (HC[a+1][b] - '0');//利用位或TempByte左移一位并写入一位BitSize++;offset--;if (offset == 0) {//字节8位已填满offset = 8;//重置为8位fwrite(&TempByte, sizeof(unsigned char), 1, f2);TempByte = 0;}}}if (offset != 8) {//若最后一个字节用不完,也要强行用到8位TempByte <<= offset;fwrite(&TempByte, sizeof(unsigned char), 1, f2);}fwrite(&BitSize, sizeof(unsigned long long), 1, f2);//将位数写在文件的最后fclose(f1);fclose(f2);free(HT);free(HC);free(CharSet);free(WeightSet);printf("已成功压缩!");
}

压缩后的文件内容按顺序为:文件的字符种数count、哈夫曼树HT、字符表CharSet、权值表WeightSet、压缩后内容、文件的总位数

解压函数

void Decompress(char in_file_name[], char out_file_name[]) {int count;FILE * f1, * f2;//f1为in,f2为out// HuffmanCode HC;HuffmanTree HT;unsigned long long BitSize;unsigned char* CharSet;unsigned int* WeightSet;fopen_s(&f1, in_file_name, "rb");//用“rb”if (!f1)exit(ERROR);fread(&count, sizeof(int), 1, f1);//读取字符种数countHT = (HuffmanTree)malloc(2 * count * sizeof(HTNode));CharSet = (unsigned char*)malloc(count * sizeof(unsigned char));WeightSet = (unsigned int*)malloc(count * sizeof(unsigned int));if ((!HT) || (!CharSet) || (!WeightSet))exit(ERROR);fread(HT, sizeof(HTNode), 2 * count, f1);//读取哈夫曼树fread(CharSet, sizeof(unsigned char), count, f1);//读取字符表fread(WeightSet, sizeof(unsigned int), count, f1);//读取权值表//  Create_HT_Codelist(HT, HC, count);//建立编码表fseek(f1, -1L * sizeof(unsigned long long), SEEK_END);//跳到末尾fread(&BitSize, sizeof(unsigned long long), 1, f1);//读取位数//跳过文件头fseek(f1, sizeof(int) + 2 * count * sizeof(HTNode) + count * sizeof(unsigned char) + count * sizeof(unsigned int), SEEK_SET);//fseek(Enc, (long)(sizeof(unsigned char) + 2 * count * sizeof(HTNode) + count * sizeof(unsigned char) + count * sizeof(unsigned int)), SEEK_SET);unsigned char TempByte = 0;unsigned char ReadByte;int offset = 8;int Index = 2 * count - 1;//根节点fopen_s(&f2, out_file_name, "wb");//打开目标文件if (!f2)exit(ERROR);fread(&ReadByte, sizeof(unsigned char), 1, f1);for (unsigned long long a = 0; a < BitSize; a++) {TempByte = 1 & (ReadByte >> 7);//位与判断ReadByte的位为1还是0if (TempByte)//从根结点开始寻找Index = HT[Index].rchild;elseIndex = HT[Index].lchild;if ((!HT[Index].lchild) && (!HT[Index].rchild)) {//遇到了根节点fwrite(&CharSet[Index - 1], sizeof(unsigned char), 1, f2);//由于HT有0号单元,故需-1Index = 2 * count - 1;}offset--;ReadByte = ReadByte << 1;//舍弃第一位if (offset == 0) {//字节的8位用完fread(&ReadByte, sizeof(unsigned char), 1, f1);offset = 8;}}free(CharSet);free(WeightSet);free(HT);fclose(f1);fclose(f2);printf("解压成功!");
}

主函数

主函数相当简单

int main() {puts("--------------------------欢迎使用Jay牌压缩程序---------------------------");puts("输入数字“1”进行压缩");puts("输入数字“2”进行解压");puts("输入数字“3”则退出");puts("--------------------------------------------------------------------------");int mark;char in_file_name[MAX_SIZE];//待压缩&&待解压文件名char out_file_name[MAX_SIZE];//解压后&&压缩后文件名for (;;) {printf("输入选项:");scanf_s("%d", &mark);getchar();//取掉回车字符switch (mark) {case 1:printf("请输入待压缩的文件路径:");//绝对还是相对路径?scanf_s("%s", in_file_name, MAX_SIZE); getchar();printf("请输入压缩后的文件路径:");scanf_s("%s", out_file_name, MAX_SIZE); getchar();Compress(in_file_name, out_file_name); break;case 2:printf("请输入待解压的文件路径:");scanf_s("%s", in_file_name, MAX_SIZE); getchar();printf("请输入压缩后的文件路径:");scanf_s("%s", out_file_name, MAX_SIZE);无getchar();Decompress(in_file_name, out_file_name); break;case 3:return 0;default:printf("请输入有效数字!");}putchar('\n');}return 0;
}

运行结果

C语言哈夫曼树压缩/解压器相关推荐

  1. C语言哈夫曼编码压缩解压

    C语言哈夫曼编码压缩解压 一.实验目的 掌握哈夫曼编码基本运算以及存储结构表示. 二.实验内容: 1.系统要求包含以下功能 1)初始化:从终端读入字符集大小n,以及n个字符和n个权值(或者读入字符集和 ...

  2. c语言哈夫曼树构造代码

    c语言哈夫曼树构造代码 博主就很掘的一个人,最近学哈夫曼树,想着用指针去实现,觉得用指针实现,内存消耗会更少,写到后面发现越来与麻烦,且内存开销并没有减少,于是还是使用结构体数组中规中矩的去实现哈夫曼 ...

  3. 文件处理小程序(包含哈夫曼文件压缩-解压等 C语言)

    文末有源代码 文件压缩.解压.加密(异或加密).解密功能等都实现了,但是处理后文件命名有点不合理,采用了加前缀的方法得到处理后的文件名,应该是改变后缀名. 应该这种更好:test.txt --- &g ...

  4. C语言霍夫曼编码压缩,数据结构大作业——哈夫曼编码压缩BMP格式文件

    数据结构大作业--哈夫曼编码压缩BMP格式文件 首先需要了解BMP图像格式 BMP图像格式详解 其次需要了解哈夫曼编码如何对BMP文件进行压缩 哈夫曼压缩与解压缩 编程部分 使用的头文件 虽然这里用了 ...

  5. 《数据结构与算法》(十一)- 树、森林与二叉树的转换及哈夫曼树详解

    目录 前言 1. 树.森林与二叉树之间的转换 1.1 树转换为二叉树 1.2. 森林转换为二叉树 1.3. 二叉树转换为树 1.4 二叉树转换为森林 1.5 树与森林的遍历 2. 哈夫曼树及其应用 2 ...

  6. 数据结构之赫夫曼文件压缩解压

    一.文件压缩 具体要求:给你一个图片文件,要求对其进行无损压缩, 看看压缩效果如何. 思路:读取文件-> 得到赫夫曼编码表 -> 完成压缩 package com.ws.数据结构.树.赫夫 ...

  7. 【赫夫曼树详解】赫夫曼树简介及java代码实现-数据结构07

    赫夫曼树(最优二叉树) 1. 简介 定义: 赫夫曼树是n个带权叶子结点构成的所有二叉树中,带权路径长度(WPL)最小的二叉树. 叶子结点的带权路径: 叶子结点权值*到根节点的路径长度(叶结点的层数) ...

  8. c语言赫夫曼树的编码与译码,哈夫曼树与编码译码实现

    一.哈弗曼树的基本概念. 哈夫曼树,又称最优树,是一类带权路径长度最短的树.下面有几个概念: (1)路径. 树中一个结点到另一个结点之间的分支构成这两个结点之间的路径. (2)路径长度. 路径上的分枝 ...

  9. c语言哈夫曼树统计字母频率,C语言实现哈夫曼树

    本文实例为大家分享了C语言实现哈夫曼树的具体代码,供大家参考,具体内容如下 //哈夫曼树C语言实现 #include #include typedef struct HuffmanNode { cha ...

最新文章

  1. 技术图文:字典技术在求解算法题中的应用
  2. 织梦配置多个mysql_一台机器,多个mysqld服务
  3. html中scope的作用,AngularJS 作用域(Scope)
  4. Effective C++学习第七天
  5. 关于Application.Lock和Lock(obj)
  6. hive 2.3 mysql_Note23:Hive-2.3.6安装配置
  7. sublime text3 插件安装
  8. 在“后台反复读取用户相册”?微信回应:最新版本将取消
  9. IP地址库Linux系统从APNIC获取地址库
  10. 过来康康,一起来学VScode插件
  11. jade的基本使用方法
  12. 高频板和普通PCB板的区别
  13. 高速低功耗视觉理解挑战赛-PRCV2020竞赛网站发布
  14. 云计算是什么?它有哪些形式?
  15. 比 rm -rf /* 还骚的 Linux 19 个装 B 的命令!
  16. PCI与PCIe学习一——硬件篇
  17. html樱花飘落特效js
  18. Java爬虫代码示例|入门教程 1- 快速爬取百度美图
  19. 运算放大器单电源供电和双电源供电
  20. Java8 Lambda表达式语法和示例

热门文章

  1. 产品学习常用数据统计网站
  2. 她的留言回复让我对中国IT的未来充满了希望
  3. oauth认证中容易被错误使用而导致的漏洞
  4. java gridlayout宽度_java-ee – 无法使gridlayout适应屏幕大小(Vaadin 6.7.4)
  5. 国内外IP线路测试网址收藏
  6. C++20 Modules
  7. shopee的八个站点,新手从哪开始?
  8. 【013】基于Vue的酒店客房管理系统(含管理员、普通用户两种身份(附源码数据库、课设报告)
  9. RationalDMIS 2020 自动测量圆柱
  10. 正点原子无线串口LORA模块透明传输学习整理