文章目录
实验目的
1. 掌握树的存储结构
2. 掌握二叉树的三种遍历方法
3. 掌握 Huffman 树、Huffman 编码等知识和应用
4. 使用 C++、文件操作和 Huffman 算法实现“图片压缩程序”专题编程。
主要仪器设备及耗材
1.安装了Windows 10操作系统的PC机1台
2.PC机系统上安装了Microsoft Visual Studio 2019开发环境
以下是本篇文章正文内容,下面案例可供参考
一、实验要求
针对一幅 BMP 格式的图片文件,统计 256 种不同字节的重复次数,以每种字节重复次数作为权值,构造一颗有 256 个叶子节点的哈夫曼二叉树。
利用上述哈夫曼树产生的哈夫曼编码对图片文件进行压缩。
压缩后的文件与原图片文件同名,加上后缀.huf(保留原后缀),如 pic.bmp,压缩后 pic.bmp.huf。
二、分析与设计
依据上述的实验目的与要求,可导出实现的二叉树与赫夫曼图片压缩软件的流程为:
① 读取图片文件、统计权值
② 生成 Huffman 树
③ 生成 Huffman 编码
④ 压缩图片文件
⑤ 保存压缩的文件
1.数据结构的设计
代码如下(示例):
//定义存储Huffman树的结构体
typedef struct HTNode
{
int weight;//权值
int parent;//父亲节点
int lchild;//左孩子
int rchild;//右孩子
}HTNode, *HuffmanTree;
typedef char** HuffmanCode;//定义哈夫曼编码类型
//文件头
struct HEAD
{
char type[4];//文件类型
int length;//原文件长度
int weight[256];//权值
};
2.核心算法设计
生成Huffman树的算法
代码如下(示例):
void CreateHuffmanTree(HuffmanTree &HT,int* a, int n)//构造哈夫曼树
{
//初始化
HT = new HTNode[2 * n ]; //动态申请2n个空间 ,0号未使用
for (int i = 1; i <= 2 * n-1 ; i++) {
//双亲 ,左右孩子置0
HT[i].parent = HT[i].lchild = HT[i].rchild = 0;
}
for (int i = 1; i <= n; i++) {
//初始化前n个的权值
HT[i].weight = a[i-1];
}
for (int i = n + 1; i <= 2 * n - 1; i++)HT[i].weight = 0;
//选取与合并,删除与加入
for (int k = n+1; k <= 2 * n - 1; k++) {
int s1, s2;
Select(HT, k-1, s1, s2);//选取两个权值最小值的位置
HT[s1].parent = k;
HT[s2].parent = k; //修改父节点
HT[k].lchild = s1; //更新左孩子权值
HT[k].rchild = s2; //更新右孩子权值
HT[k].weight = HT[s1].weight + HT[s2].weight; //更新自己的权值
}
}
//选取两个权值最小值的位置
void Select(HuffmanTree& HT, int i, int& s1, int& s2) {
int minValue = 0x7FFFFFFF;
//找到最小的一个权值
for (int j = 1; j <= i; j++) {
if (HT[j].parent == 0 && HT[j].weight < minValue) {
minValue =HT[j].weight;
s1 = j;
}
}
// cout << "最小权值:" << s1;
minValue = 0x7FFFFFFF;
//找到倒数第二小的权值
for (int j = 1; j <= i; j++) {
if (j != s1 && HT[j].parent == 0 && HT[j].weight < minValue) {
minValue = HT[j].weight;
s2 = j;
}
}
// cout << "\t次之权值:" << s2 << endl;
}
生成Huffman编码的算法
代码如下(示例):
//生成Huffman编码
void HuffmanCoding(HuffmanCode& HC, HuffmanTree& HT)
{
//无栈非递归遍历Huffman树,求Huffman编码
char cd[256] = {
'\0' }; //记录访问路径
int cdlen = 0; //记录当前路径长度
for (int i = 1; i < 512; i++) HT[i].weight = 0; //遍历Huffman树时用做节点的状态标志
int p = 511; //根节点
while (p != 0) {
//向左
if (HT[p].weight == 0) {
HT[p].weight = 1;
if (HT[p].lchild != 0) {
p = HT[p].lchild;
cd[cdlen++] = '0';
}
//登记叶子节点的字符的编码
else if (HT[p].rchild == 0) {
HC[p] = (char*)malloc((cdlen + 1) * sizeof(char));
cd[cdlen] = '\0';
strcpy(HC[p], cd);//复制编码
}
}
//向右
else if (HT[p].weight == 1) {
HT[p].weight = 2;
//右孩子为叶子节点
if (HT[p].rchild != 0) {
p = HT[p].rchild;
cd[cdlen++] = '1';
}
}
//退回父节点,编码长度减一
else {
HT[p].weight = 0;
p = HT[p].parent;
cdlen--;
}
}
}
压缩编码的算法
代码如下(示例):
//实现压缩编码
int Encode(const char* Filename, const HuffmanCode HC, char* Buffer, const int nSize) {
FILE* in = fopen(Filename, "rb");//打开文件
//开辟缓冲区
Buffer = (char*)malloc(nSize * sizeof(char));
if (!Buffer) {
cerr << "开辟缓冲区失败" << endl;//输出错误信息
return 0;
}
char cd[256] = {
0 };//工作区
int pos = 0;//缓冲区指针
int c;
//扫描文件进行哈夫曼压缩,结果存入缓冲区
while ((c = getc(in)) != EOF) {
strcat(cd, HC[c+1]);//复制HC到cd工作区里
//压缩编码
while (strlen(cd) >= 8) {
Buffer[pos++] = Str2byte(cd);
//截取字符串左边的8个字符,编码成字节
for (int i = 0; i < 256 - 8; i++)cd[i] = cd[i + 8];
//字符串左移8字节
}
}
if (strlen(cd) > 0)Buffer[pos++] = Str2byte(cd);
fclose(in);//关闭文件
return 1;//成功
}
3.测试用例设计
任意形成一个BMP图片文件,观察其压缩比;
在互联网上搜索下载任意格式的图片文件,例如JPG格式,观察其压缩比。
4.测试效果
总结
详情可参考以下链接
链接:冲冲冲~
提取码:bwbw
复制这段内容后打开百度网盘手机App,操作更方便哦