【数据结构与算法综合实验】二叉树与赫夫曼图片压缩实践

本文主要是介绍【数据结构与算法综合实验】二叉树与赫夫曼图片压缩实践,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

说明:这是武汉理工大学计算机学院数据结构与算法综合实验课程的第一次项目:二叉树与赫夫曼图片压缩实践代码。

>>点击查看武汉理工大学计算机专业课程资料汇总

源码+实验报告下载地址(运行环境:VS2017):

1.  面包多平台下载:https://mianbaoduo.com/o/bread/YZWcm5tq

2. 公众号文章下载:https://mp.weixin.qq.com/s/yBc4T9B4G4Tp5ufHglzXww

3. CSDN下载:https://download.csdn.net/download/cxh_1231/10450976

笔者本科资料汇总:https://blog.csdn.net/cxh_1231/article/details/112465790

纸上得来终觉浅,绝知此事要躬行。

实验教材:

任务列表:

阶段

工作内容

交付物

参考资料

实验一

二叉树与赫夫曼图片压缩

上机实验代码

《数据结构和C++编程》第5章 树和二叉树

实验内容:

1、创建工程。

2、读取源文件。

3、生成哈夫曼树。

4、生成哈夫曼编码。

5、压缩原文件。

6、保存压缩文件。

7、扩展功能。

项目截图:

实验代码:

Huffman.h 头文件代码:

#ifndef HUFFMAN_H
#define HUFFMAN_H//Huffman树节点
typedef struct 
{int weight;	//权值int parent;	//父节点int lchild;	//左孩子int rchild;	//右孩子
}HTNode, *HuffmanTree;typedef char **HuffmanCode;		//Huffman编码//生成Huffman树
int CreateHuffmanTree(HuffmanTree pHT,int weight[],int n);
int CreateHuffmanTree2(HuffmanTree &pHT, int w[], int n);//生成Huffman编码
int HuffmanCoding(HuffmanCode &pHC, HuffmanTree  &pHT);int Select(HuffmanTree pHT, int nSize);
void Select(HuffmanTree &HT, int i, int&s1, int&s2);int TestHufTree(HuffmanTree pHT);
void TestHufCode(int root, HuffmanTree &pHT, HuffmanCode &pHC);#endif

Huffman.cpp 源文件代码:

#include "Huffman.h"
#include<iostream>
#include<malloc.h>#define OK 1
#define ERROR 0using namespace std;//生成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;}//	cout << "测试初始化Huffman树后的列表" << endl;
//	if (TestHufTree(pHT))
//		cout << "测试完毕!" << endl;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; //修改权值
//		cout << "257以及以后 修改权值后的值:" << i << "\t" << pHT[i].weight << endl;}//	cout << "测试生成Huffman树后的列表" << endl;
//	if (TestHufTree(pHT))
//		cout << "测试完毕!" << endl;return OK;
}int CreateHuffmanTree2(HuffmanTree &pHT, int w[], int n)
{//w存放n个结点的权值,将构造一棵哈夫曼树pHTint i, m;int s1, s2;//	HuffmanTree p;if (n <= 1) return ERROR;m = 2 * n - 1; //n个叶子结点的哈夫曼树,有2*n-1个结点pHT = (HuffmanTree)malloc((m +1 ) * sizeof(HTNode)); //开辟2*n各结点空间,0号单元未用for ( i = 1; i <= n; ++i) //进行初始化{pHT[i].weight = w[i];pHT[i].parent = 0;pHT[i].lchild = 0;pHT[i].rchild = 0;}for (; i <= m; ++i){pHT[i].weight = 0;pHT[i].parent = 0;pHT[i].lchild = 0;pHT[i].rchild = 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树节点数组中权值最小的节点
int Select(HuffmanTree pHT, int nSize) {int minValue = 0x7FFFFFFF;	//最小值int min = 0;				//元素序号//找到最小权值的元素序号for (int i = 1; i <= nSize; i++){if (pHT[i].parent == 0 && pHT[i].weight<minValue){minValue = pHT[i].weight;min = i;}}return min;
}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;}}
//	cout << "最小权值:" << s1;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;}}
//	cout << "\t次之权值:" << s2 << endl;
}//生成Huffman编码 
int HuffmanCoding(HuffmanCode &pHC, HuffmanTree &pHT)
{//无栈非递归遍历Huffman树,求Huffman编码char cd[256] = { '\0' };	//记录访问路径int cdlen = 0;				//记录当前路径长度for (int i = 1; i < 512; i++) {pHT[i].weight = 0;	//遍历Huffman树时用做节点的状态标志}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 << "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;}else {cout << "pHT[" << i << "]\t" << pHT[i].weight << "\t" << pHT[i].parent << "\t" << pHT[i].lchild << "\t" << pHT[i].rchild << endl;}}return OK;
}void TestHufCode(int root, HuffmanTree &pHT, HuffmanCode &pHC) {if (root <= 1) return;if (pHT[root].lchild == 0 && pHT[root].rchild == 0){printf("0x%02X %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);}}

Compress.h 头文件代码:

#ifndef COMPRESS_H
#define COMPRESS_H
//typedef char **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 *, char**, char *, const int);
//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);//测试相关
int TestWeight(int weight[]);#endif

Compress.cpp 源文件代码:

#include <iostream>
#include <stdlib.h>
#include "Compress.h"
#include"Huffman.h"using namespace std;#define OK 1
#define ERROR 0const int SIZE = 256;//扫描文件和初始化头文件的信息
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 Compress(const char * pFilename)
{/**************************************************///打开并扫描文件cout << "正在读取文件……" << endl;int weight[256] = { 0 };  //打开文件,获取权重FILE* in = fopen(pFilename, "rb");int tempch;while ((tempch = getc(in)) != EOF){weight[tempch]++;}//测试扫描结果:显示256种字节出现的次数//if (TestWeight(weight))//	cout << "测试完毕!!!" << endl << endl;cout << "文件读取完毕!\n" << endl<<endl;fclose(in);/**************************************************///将编码生成Huffman树int i;int n = 256;						//Huffman树共有n个叶子节点int m = 2 * n - 1;					//那么就有2n+1个节点HuffmanTree pHT = new HTNode[m + 1];	//定义Huffman树CreateHuffmanTree(pHT,weight,n);//	cout << "测试生成的Huffman树" << endl;
//	if (TestHufTree(pHT))
//		cout << "测试完毕!" << endl;//生成Huffman编码char** pHC = new char*[n + 1]; //编码for (int i = 1; i <= n; i++)pHT[i].weight = weight[i - 1];//	cout << "测试生成的Huffman树" << endl;//测试生成的Huffman树//cout << "生成Huffman树测试过程:\n";//if (TestHufTree(pHT))//	cout << "测试完毕!" << endl;HuffmanCoding(pHC, pHT);//cout << "测试:显示字节的Huffman编码" << endl;//cout << "Byte\tHuffmanCode" << endl;//TestHufCode(511, pHT, pHC);/**************************************************///计算编码缓冲区大小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 << "字节  \n其中头文件sHead大小:" << sizeof(sHead) << "字节" << endl;cout << "压缩比率:" << (double)afterlen * 100 / sHead.length << "%" << endl;delete pHT; delete[] pHC; delete pBuffer;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;
}int TestWeight(int weight[]) {cout << "显示256种字节出现的次数:" << endl;cout << "Byte\t" << "Weight\t" << endl;for (int i = 0; i < 256; i++) {printf("0x%02X\t%d\n",i, weight[i]);//cout << i << "\t" << weight[i] << endl;}return OK;
}

Main.cpp 主函数代码:

// Main.cpp: 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include "Compress.h"
#include"Huffman.h"
using namespace std;int main()
{cout << "=========Huffman文件压缩=======" << endl;cout << "请输入文件名:";char filename[256];cin >> filename;if (Compress(filename) == 1) {cout << "\n压缩完成!" << endl;}else {cout << "\n压缩失败" << endl;}return 0;
}

运行结果:

实验报告:

后续补充

这篇关于【数据结构与算法综合实验】二叉树与赫夫曼图片压缩实践的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Docker集成CI/CD的项目实践

《Docker集成CI/CD的项目实践》本文主要介绍了Docker集成CI/CD的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、引言1.1 什么是 CI/CD?1.2 docker 在 CI/CD 中的作用二、Docke

使用 Python 和 LabelMe 实现图片验证码的自动标注功能

《使用Python和LabelMe实现图片验证码的自动标注功能》文章介绍了如何使用Python和LabelMe自动标注图片验证码,主要步骤包括图像预处理、OCR识别和生成标注文件,通过结合Pa... 目录使用 python 和 LabelMe 实现图片验证码的自动标注环境准备必备工具安装依赖实现自动标注核心

Java操作xls替换文本或图片的功能实现

《Java操作xls替换文本或图片的功能实现》这篇文章主要给大家介绍了关于Java操作xls替换文本或图片功能实现的相关资料,文中通过示例代码讲解了文件上传、文件处理和Excel文件生成,需要的朋友可... 目录准备xls模板文件:template.xls准备需要替换的图片和数据功能实现包声明与导入类声明与

Qt实现文件的压缩和解压缩操作

《Qt实现文件的压缩和解压缩操作》这篇文章主要为大家详细介绍了如何使用Qt库中的QZipReader和QZipWriter实现文件的压缩和解压缩功能,文中的示例代码简洁易懂,需要的可以参考一下... 目录一、实现方式二、具体步骤1、在.pro文件中添加模块gui-private2、通过QObject方式创建

基于C#实现将图片转换为PDF文档

《基于C#实现将图片转换为PDF文档》将图片(JPG、PNG)转换为PDF文件可以帮助我们更好地保存和分享图片,所以本文将介绍如何使用C#将JPG/PNG图片转换为PDF文档,需要的可以参考下... 目录介绍C# 将单张图片转换为PDF文档C# 将多张图片转换到一个PDF文档介绍将图片(JPG、PNG)转

Qt QWidget实现图片旋转动画

《QtQWidget实现图片旋转动画》这篇文章主要为大家详细介绍了如何使用了Qt和QWidget实现图片旋转动画效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、效果展示二、源码分享本例程通过QGraphicsView实现svg格式图片旋转。.hpjavascript

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第