文章目录

  • 一、实验要求
  • 二、效果展示
  • 三、源码
    • 3.1、Compress.cpp
    • 3.2、Compress.h
    • 3.3、global.h
    • 3.4、Huffman.cpp
    • 3.5、Huffman.h
    • 3.6、main.cpp

一、实验要求

压缩软件是利用特定算法来压缩数据的工具,压缩后生成的文件称为压缩包(archive)。如果想使用其中的数据,就得用压缩软件对数据进行解压。利用压缩软件对文件中重复的数据进行压缩,可以减小文件中的字节总数,使文件能够通过互联网连接实现更快传输,此外还可以减少文件的磁盘占用空间。常用的压缩软件有 rar、zip 等。

压缩可以分为无损压缩与有损压缩两种。无损压缩后的文件,经过解压能够完全恢复原始数据;有损压缩的文件则无法完全恢复。rar、zip 等格式都是无损压缩格式。音乐文件格式 mp3、图片文件格式 jpg 都是有损压缩格式。

计算机文件是由一个个字节组成的,1 个字节有 0~255 共 256 种可能的值,每个字节的编码长度都是 8 位。由于文件中的字节总是会重复出现,可以对不同的字节设计长度不等的编码,让出现次数较多的字节,采用尽可能短的编码,那么文件编码的总长便可减
少。

统计文件中 256 种不同的字节重复的次数,以每种字节重复的次数作为权值(weight),构造一棵有 256 个叶子节点的二叉树。若带权路径长度达到最小,称这样的二叉树为最优二叉树,即 Huffman 树(Huffman tree)。

Huffman 树从根到每个叶子都有一条路径。对路径上的各分支,约定指向左子树根的分支编码为“0”,指向右子树根的分支编码为“1”。从根到每个叶子相应路径上的“0”和“1”组成的序列,就是这个叶子节点的编码,称为 Huffman 编码。

功能要求:使用 Huffman 压缩算法,对一幅 BMP 格式的图片文件进行压缩。图片文件名为“Pic.bmp”,内容如下图所示。压缩后保存为“Pic.bmp.huf”文件。

二、效果展示

三、源码

3.1、Compress.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdlib.h>
#include "Compress.h"
#include"Huffman.h"
#include"global.h"using namespace std;;const int SIZE = 256;//实现文件压缩
int Compress(const char* pFilename)
{cout << endl;//打开并扫描文件cout << "正在读取文件……" << endl << endl;int weight[256] = { 0 };FILE* in = fopen(pFilename, "rb");int tempch;//获取权重while ((tempch = getc(in)) != EOF)weight[tempch]++;int temp;cout << "输入数字“1”显示256种字节出现次数,输入其它数字则不显示!" << endl;cout << "请输入您的选择:";cin >> temp;cout << endl;if (temp == 1) {//测试,显示256种字节出现的次数showWeight(weight);cout << endl;}cout << "文件读取完毕!\n" << endl;//关闭文件fclose(in);//将编码生成Huffman树//Huffman树共有n个叶子节点int n = 256;//那么就有2n+1个节点int m = 2 * n - 1;//定义Huffman树HuffmanTree pHT = new HTNode[m + 1];CreateHuffmanTree(pHT, weight, n);int temp1;cout << "输入数字“1”显示Huffman 树每个节点的信息,输入其它数字则不显示!" << endl;cout << "请输入您的选择:";cin >> temp1;cout << endl;if (temp1 == 1) {//测试,输出 Huffman 树每个节点的信息TestHufTree(pHT);cout << endl;}//生成Huffman编码char** pHC = new char* [n + 1]; //编码for (int i = 1; i <= n; i++)pHT[i].weight = weight[i - 1];HuffmanCoding(pHC, pHT);int temp2;cout << "输入数字“1”显示字节的Huffman编码信息,输入其它数字则不显示!" << endl;cout << "请输入您的选择:";cin >> temp2;cout << endl;if (temp2 == 1) {//测试,显示字节的Huffman编码信息cout << "\n哈夫曼树的编码信息为:" << endl;cout << "Byte\tHuffmanCode" << endl;TestHufCode(511, pHT, pHC);cout << endl;}//计算编码缓冲区大小int nSize = 0;for (int i = 0; i < 256; i++)nSize += weight[i] * strlen(pHC[i + 1]);nSize = (nSize % 8) ? nSize / 8 + 1 : nSize / 8;//对编码文件进行压缩char* pBuffer = NULL;pBuffer = new char[nSize];memset(pBuffer, 0, (nSize) * sizeof(char));Encode(pFilename, pHC, pBuffer, nSize);if (!pBuffer) {return ERROR;}HEAD sHead;InitHead(pFilename, sHead);cout << "原文件大小:" << sHead.length << "字节" << endl;int afterlen = WriteFile(pFilename, sHead, pBuffer, nSize);cout << "压缩后文件大小:" << afterlen << "字节" << endl;cout << "压缩比率:" << (double)afterlen * 100 / sHead.length << "%" << endl;delete pHT;delete[] pHC;delete pBuffer;return OK;
}//扫描文件和初始化头文件的信息
int InitHead(const char* pFilname, HEAD& sHead)
{//文件类型strcpy(sHead.type, "HUF");//源文件长度sHead.length = 0;for (int i = 0; i < SIZE; i++)//权值sHead.weight[i] = 0;//以二进制流形式打开文件FILE* in = fopen(pFilname, "rb");//扫描文件,获得权重int ch;while ((ch = fgetc(in)) != EOF) {sHead.weight[ch]++;sHead.length++;}//关闭文件fclose(in);in = NULL;return OK;
}//实现压缩编码
int Encode(const char* pFilname, const HuffmanCode pHC, char* pBuffer, const int nSize)
{//打开文件FILE* in = fopen(pFilname, "rb");//开辟缓冲区nipBuffer = (char*)malloc(nSize * sizeof(char));if (!pBuffer)cout << "开辟缓冲区失败!" << endl;//工作区char cd[SIZE] = { 0 };//缓冲区指针int pos = 0;int ch;//扫描文件while ((ch = fgetc(in)) != EOF) {strcat(cd, pHC[ch + 1]);//压缩编码while (strlen(cd) >= 8) {pBuffer[pos++] = Str2byte(cd);for (int i = 0; i < SIZE - 8; i++) {cd[i] = cd[i + 8];}}}if (strlen(cd) > 0) {pBuffer[pos++] = Str2byte(cd);}fclose(in);return OK;
}//生成压缩文件
int WriteFile(const char* pFilename, const HEAD sHead, const char* pBuffer, const int nSize)
{//生成文件名char filename[256] = { 0 };strcpy(filename, pFilename);strcat(filename, ".huf");//以二进制流形式打开文件FILE* out = fopen(filename, "wb");//写文件fwrite(&sHead, sizeof(HEAD), 1, out);//写压缩后的编码fwrite(pBuffer, sizeof(char), nSize, out);//关闭文件,释放文件指针fclose(out);out = NULL;cout << "生成压缩文件:" << filename << endl;int len = sizeof(HEAD) + strlen(pFilename) + 1 + nSize;return len;
}//将字符串转换成字节
char Str2byte(const char* pBinStr)
{char b = 0x00;for (int i = 0; i < 8; i++) {//左移一位b = b << 1;if (pBinStr[i] == '1') {b = b | 0x01;}}return b;
}

3.2、Compress.h

#pragma once
#include"Huffman.h" //Encode函数声明会用到HuffmanCode//文件头
struct HEAD
{char type[4];int length;int weight[256];
};//实现文件压缩
int Compress(const char* pFilename);//读取源文件和初始化头文件的信息
int InitHead(const char* pFilname, HEAD& sHead);//利用Huffman编码 实现压缩编码
int Encode(const char* pFilname, const HuffmanCode pHC, char* pBuffer, const int nSize);//将二进制字符串转换成字节
char Str2byte(const char* pBinStr);//生成压缩文件
int WriteFile(const char* pFilename, const HEAD sHead, const char* pBuffer, const int nSize);

3.3、global.h

#pragma once#define OK 1
#define ERROR 0

3.4、Huffman.cpp

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<malloc.h>
#include"Huffman.h"
#include"global.h"using namespace std;// 显示 256 种字节的出现的次数
void showWeight(int weight[])
{cout << "原文件每个字符的权值为:" << endl;cout << "Byte\t" << "Weight\t" << endl;for (int i = 0; i < 256; i++)printf("0x%02X\t%d\n", i, weight[i]);
}//生成Huffman树
int CreateHuffmanTree(HuffmanTree pHT, int weight[], int n)
{int s1, s2, i;int m = 2 * n - 1;//初始化for (i = 1; i <= n; i++) {pHT[i].weight = weight[i - 1];pHT[i].lchild = 0;pHT[i].rchild = 0;pHT[i].parent = 0;}for (i = n + 1; i <= m; i++) {pHT[i].parent = 0;pHT[i].lchild = 0;pHT[i].rchild = 0;pHT[i].weight = 0;}for (i = n + 1; i <= m; i++) {//从pHT[1...i-1]中选择parent为0且weight最小的两个结点,其序号分别为s1和s2Select(pHT, i - 1, s1, s2);pHT[s1].parent = i;pHT[s2].parent = i; //修改s1和s2结点的父指针parentpHT[i].lchild = s1;pHT[i].rchild = s2; //修改i结点的左右孩子指针pHT[i].weight = pHT[s1].weight + pHT[s2].weight; //修改权值}return OK;
}//查找Huffman树节点数组中权值最小的节点
void Select(HuffmanTree& pHT, int i, int& s1, int& s2)
{int minValue = 0x7FFFFFFF;//找到最小的一个权值for (int j = 1; j <= i; j++) {if (pHT[j].parent == 0 && pHT[j].weight < minValue) {minValue = pHT[j].weight;s1 = j;}}minValue = 0x7FFFFFFF;//找到倒数第二小的权值for (int j = 1; j <= i; j++) {if (j != s1 && pHT[j].parent == 0 && pHT[j].weight < minValue) {minValue = pHT[j].weight;s2 = j;}}
}//生成Huffman编码
int HuffmanCoding(HuffmanCode& pHC, HuffmanTree& pHT)
{//无栈非递归遍历Huffman树,求Huffman编码//记录访问路径char cd[256] = { '\0' };//记录当前路径长度int cdlen = 0;//遍历Huffman树时用做节点的状态标志for (int i = 1; i < 512; i++)pHT[i].weight = 0;int p = 511;while (p != 0) {//向左if (pHT[p].weight == 0) {pHT[p].weight = 1;if (pHT[p].lchild != 0) {p = pHT[p].lchild;cd[cdlen++] = '0';}//登记叶子节点的字符的编码else if (pHT[p].rchild == 0) {pHC[p] = (char*)malloc((cdlen + 1) * sizeof(char));cd[cdlen] = '\0';strcpy(pHC[p], cd);//复制编码}}//向右else if (pHT[p].weight == 1) {pHT[p].weight = 2;//右孩子为叶子节点if (pHT[p].rchild != 0) {p = pHT[p].rchild;cd[cdlen++] = '1';}}//退回父节点,编码长度减一else {pHT[p].weight = 0;p = pHT[p].parent;cdlen--;}}return OK;
}//测试函数,输出哈夫曼树的每个节点信息
int TestHufTree(HuffmanTree pHT) {cout << "哈夫曼树的每个节点信息为:" << endl;cout << "Byte\t\tWeight\tParent\tLchild\tRchild\n";for (int i = 1; i < 512; i++) {//判断语句为了对齐格式if (i <= 99)cout << "pHT[" << i << "]\t\t" << pHT[i].weight << "\t" << pHT[i].parent << "\t" << pHT[i].lchild << "\t" << pHT[i].rchild << endl;elsecout << "pHT[" << i << "]\t" << pHT[i].weight << "\t" << pHT[i].parent << "\t" << pHT[i].lchild << "\t" << pHT[i].rchild << endl;}return OK;
}//测试函数,采用先序遍历的方法,输出 Huffman 树每个节点的信息
void TestHufCode(int root, HuffmanTree& pHT, HuffmanCode& pHC)
{if (root <= 1) return;if (pHT[root].lchild == 0 && pHT[root].rchild == 0)printf("0x%02X\t%s\n", root - 1, pHC[root - 1]);if (pHT[root].lchild)//访问左孩子TestHufCode(pHT[root].lchild, pHT, pHC);if (pHT[root].rchild)//访问右孩子TestHufCode(pHT[root].rchild, pHT, pHC);
}

3.5、Huffman.h

#pragma once//Huffman树节点
typedef struct
{int weight;    //权值int parent; //父节点int lchild;    //左孩子int rchild;    //右孩子
}HTNode, * HuffmanTree;//Huffman编码
typedef char** HuffmanCode;// 显示 256 种字节的出现的次数
void showWeight(int weight[]);//生成Huffman树
int CreateHuffmanTree(HuffmanTree pHT, int weight[], int n);//生成Huffman编码
int HuffmanCoding(HuffmanCode& pHC, HuffmanTree& pHT);//查找 Huffman 树结点数组中权值最小的节点
void Select(HuffmanTree& HT, int i, int& s1, int& s2);//测试函数,输出哈夫曼树的每个节点信息
int TestHufTree(HuffmanTree pHT);//测试函数,采用先序遍历的方法,输出 Huffman 树每个节点的信息
void TestHufCode(int root, HuffmanTree& pHT, HuffmanCode& pHC);

3.6、main.cpp

#include<iostream>
#include"Compress.h"
using namespace std;int main(void) {cout << "========== Huffman 文件压缩 ==========" << endl;cout << "请输入文件名:";char  filename[256];cin >> filename;if (Compress(filename) == 1)cout << "\n文件压缩成功!" << endl;elsecout << "\n文件压缩失败!" << endl;return 0;
}

[数据结构与算法综合实验]二叉树与哈夫曼图片压缩相关推荐

  1. 武汉理工大学数据结构综合实验——二叉树与赫夫曼图片压缩

    文章目录 实验目的 主要仪器设备及耗材 一.实验要求 二.分析与设计 1.数据结构的设计 2.核心算法设计 生成Huffman树的算法 生成Huffman编码的算法 压缩编码的算法 3.测试用例设计 ...

  2. 本科课程【数据结构与算法】实验4—— 构造哈夫曼树、深度优先搜索

    大家好,我是[1+1=王], 热爱java的计算机(人工智能)渣硕研究生在读. 如果你也对java.人工智能等技术感兴趣,欢迎关注,抱团交流进大厂!!! Good better best, never ...

  3. 【数据结构与算法实验】二叉树与哈夫曼图片压缩

    使用Huffman压缩算法,对一幅BMP格式的图片文件进行压缩.图片文件名为"Pic.bmp",压缩后保存为"Pic.bmp.huf"文件. 程序截图: Mai ...

  4. 二叉树与哈夫曼图片压缩(c++)

    开发一个控制台程序,使用Huffnan压缩算法对bmp格式图片文件进行压缩 项目结构 运行结果 Huffman.h #pragma once #include<iostream> usin ...

  5. 数据结构与算法综合实验(附完整项目)

    数据结构与算法综合实验--练练看系统 系统设计 效果展示 1. 菜单界面 2.游戏界面 3.游戏结束界面 开源链接 linkGame代码传送门

  6. 【数据结构与算法综合实验】景区信息管理系统迭代开发

    说明:这是武汉理工大学计算机学院数据结构与算法综合实验课程的第二次项目:景区信息管理系统迭代开发代码. >>点击查看武汉理工大学计算机专业课程资料汇总 源码+实验报告下载地址(运行环境:V ...

  7. 【数据结构与算法综合实验】欢乐连连看(C++ MFC)案例

    说明:这是武汉理工大学计算机学院数据结构与算法综合实验课程的第三次项目:欢乐连连看(C++ & MFC)迭代开发代码. >>点击查看武汉理工大学计算机专业课程资料汇总 >&g ...

  8. 【课程记录·本】WUT数据结构与算法综合实验 | 基于C++MFC的欢乐连连看游戏的设计与实现(附下载链接)

    本文基于文章" https://blog.csdn.net/cxh_1231/article/details/89577820 "二次修改发布,原文已失效,不再维护 我的微信公众号 ...

  9. 数据结构与算法 -判定树和哈夫曼树

    1. 分类与判定树 判定树是用于描述分类过程的二叉 树,每个非终端结点包含一个条件,对应一次比较:每个终端结点 包含一个种类标记, 对应于一种分类结果. 设有n个学生,现要根据他们的成绩将其划分为5类 ...

最新文章

  1. postgresql select for update 多行加锁顺序_insert into select加锁规则补充
  2. CF932G Palindrome Partition
  3. Gerrit plugin安装和删除
  4. 通过 yar 初识RPC
  5. 利用JavaScript制作星星金字塔
  6. 计算机网络考试试题库-期末考试题库含答案
  7. python列表是顺序表还是链表_Python数据结构与算法(链表使用详解)
  8. if else if else语句格式_计算机各语言之间if...else区别
  9. Atitit 区块链之道 attilax著 艾龙著 1. 金融=制度+技术+信息 1 2. 第一章可信的协议 1 3. 第二章引导未来:区块链经济七大设计原则 1 4. 第五章 新商业
  10. 增程式串联混合动力实际项目模型,本模型基于Cruise软件和Simulink软件共同搭建完成
  11. 用三元组存储稀疏矩阵,实现其快速转置及矩阵相乘
  12. 计算机软件工程专业选科要求,选考科目科普:软件工程专业要求哪些选考科目?...
  13. windows 内网域电脑无法ntp时间同步
  14. 【2016NOIP十连测】【test4】【状压DP】【容斥原理】巨神兵
  15. 【采坑】CocosCreator 使用对象池再次创建对象创建节点时出现的某明的bug,节点使用了动作系统
  16. 电信院 创新创业实践二 FPGA Verilog vivado 数码显示管 显示译码器
  17. 微型计算机硬件系统基本组成一般应用包括,计算机系统的基本组成,一般应包括哪些...
  18. html页面导出文件大小,【实战】通过 JS 将 HTML 导出为 PDF 文档
  19. 云服务器和域名的购买—华为云
  20. HTML、CSS中雪碧图的使用

热门文章

  1. 微型计算机的输入输出控制方式,(微机原理)第6章微型计算机的输入输出.pptx
  2. 软件测试实验——对新冠肺炎病毒自助检测等(1)
  3. 解决iframe嵌套页面问题
  4. 转手把手教你制作Iphone户外徒步地图
  5. 电商宝SCRM微信多公众号聚合客服系统可以解决哪些痛点?
  6. 清华OJ重名剔除(Deduplicate)
  7. python_virtualenv虚拟环境
  8. 傅氏级数的各项系数_傅里叶系数的推导
  9. tinyxml2简单的创建、增、删、改、查
  10. 8位伪随机序列(m序列verilog HDL源码 )