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


实验目的

 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,操作更方便哦

猜你喜欢

转载自blog.csdn.net/mo_zhe/article/details/112765979
今日推荐