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

开发一个控制台程序,使用Huffnan压缩算法对bmp格式图片文件进行压缩

项目结构

在这里插入图片描述

运行结果

在这里插入图片描述

Huffman.h
#pragma once
#include<iostream>
using namespace std;
struct HTNode
{
	int weight=0;//权值
	int parent=0;//父节点
	int lchild=0;//左孩子
	int rchild=0;//右孩子
};

typedef HTNode *Huffmantree;
typedef char** HuffmanCode;
int HuffmanTree(Huffmantree &pHT, int* w, int n);
int Select(Huffmantree pHT, int nSize);
void TestHuffmanTree(Huffmantree pHT);
int HuffmanCoding(HuffmanCode& pHC, Huffmantree& pHT);//求Huffman编码
void TestHuffmanCode(int root, Huffmantree pHT, HuffmanCode pHC);
Compress.h
#pragma once
#include"Huffman.h"
#define SIZE 256
//文件头
struct HEAD {
	char type[8];//文件类型
	int length;//文件长度
	int weight[256]; //权值数值
};
typedef char* BUFFER;
int InitHead(const char* pFilename, HEAD& sHead); //初始化文件头
int Compress(const char* pFilename);
int Str2byte(const char* pBinStr);
int Encode(const char* pFilename, const HuffmanCode pHC, char* pBuffer, const int nSize);
int WriteFile(const char* pFilename, const HEAD sHead, const BUFFER pBuffer,const int nSize);//写文件
Huffman.cpp
#include"Huffman.h"
int HuffmanTree(Huffmantree &pHT,int* w, int n) {
	//初始化Huffman树
	for (int i = 0; i < n * 2; i++)
		pHT[i] = { 0,0,0,0 };
	for (int i = 0; i < n; i++) {
		pHT[i + 1].weight = w[i];
	}
	//生成最优二叉树
	for (int i = n; i < n * 2; i++) {
		//找出左孩子
		pHT[i + 1].lchild = Select(pHT, i);
		pHT[i + 1].weight += pHT[Select(pHT, i)].weight;
		pHT[Select(pHT, i)].parent = i + 1;
		//找出右孩子
		pHT[i + 1].rchild = Select(pHT, i);
		pHT[i + 1].weight += pHT[Select(pHT, i)].weight;
		pHT[Select(pHT, i)].parent = i + 1;
	}
	return 0;
}

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 TestHuffmanTree(Huffmantree pHT) {
	for (int i = 1; i < 512; i++) {
		printf("pHT[%d] \t %d \t %d \t %d \t %d \n", i, pHT[i].weight, pHT[i].parent, pHT[i].lchild, pHT[i].rchild);
	}
}

int HuffmanCoding(HuffmanCode& pHC, Huffmantree& pHT) {
	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 0;
}

void TestHuffmanCode(int root, Huffmantree pHT, HuffmanCode pHC) {
	if (pHT[root].lchild == 0 && pHT[root].rchild == 0) {
		printf("0x%02X %s\n", root - 1, pHC[root]);
	}
	if (pHT[root].lchild)//访问左孩子
	{
		TestHuffmanCode(pHT[root].lchild, pHT, pHC);
	}
	if (pHT[root].rchild)//访问右孩子
	{
		TestHuffmanCode(pHT[root].rchild, pHT, pHC);
	}
}
Compress.cpp
#include"Compress.h"
#include"Huffman.h"

int Compress(const char* pFilename) {
	int weight[256] = { 0 };
	FILE* in = fopen(pFilename, "rb");
	int ch;
	while ((ch = getc(in)) != EOF) {
		weight[ch]++;
	}
	fclose(in);

	HTNode* pHT = (HTNode*)malloc(512 * sizeof(HTNode));
	HuffmanCode pHC = (char**)malloc(257 * sizeof(char*));
	HuffmanTree(pHT, weight, 256);
	HuffmanCoding(pHC, pHT);

	//计算缓冲区大小
	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;
	Encode(pFilename, pHC, pBuffer, nSize);
	return 0;
}

int 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 strlen2(char* x) {
	int i = 0;
	while (x[i] == '0' || x[i] == '1') {
		i++;
	}
	return i;
}

int Encode(const char* pFilename, const HuffmanCode pHC, char* pBuffer, const int nSize) {
	//开辟缓冲区
	FILE* in = fopen(pFilename, "rb");
	pBuffer = (char*)malloc(nSize * sizeof(char));
	if (!pBuffer) {
		cerr << "开辟缓冲区失败" << endl;
		return 0;
	}
	char cd[SIZE] = { 0 };//工作区
	int pos = 0;//缓冲区指针
	int ch;
		//扫描文件,根据Huffman编码表对其进行压缩,压缩结果暂存到缓冲区中
	while ((ch = fgetc(in)) != EOF)
	{
		strcat(cd, pHC[ch+1]);

		//压缩编码
		while (strlen2(cd) >= 8) {
			//截取字符串左边的8个字符,编码成字节
			pBuffer[pos++] = Str2byte(cd);
			//字符串整体左移
			for (int i = 0; i < SIZE - 8; i++) {
				cd[i] = cd[8 + i];
			}
			
		}
	}
	if (strlen(cd) > 0) {
		pBuffer[pos++] = Str2byte(cd);
	}
	fclose(in);
	HEAD sHead;
	InitHead(pFilename, sHead);
	WriteFile(pFilename, sHead, pBuffer, nSize);
}

int InitHead(const char* pFilename, HEAD& sHead) {
	//初始化文件头
	strcpy(sHead.type, "HUF");//文件类型
	sHead.length = 0;//源文件长度
	for (int i = 0; i < SIZE; i++) {
		sHead.weight[i] = 0;//权值
	}
	//以二进制流形式打开文件
	FILE* in = fopen(pFilename, "rb");
	int ch;
	while ((ch = getc(in)) != EOF) {
		sHead.weight[ch]++;
		sHead.length++;
	}
	fclose(in);
	in = NULL;
	return 0;
}

int WriteFile(const char* pFilename, const HEAD sHead, const BUFFER pBuffer,const int nSize) {
	//生成文件名
	char filename[SIZE] = { 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 << sHead.length<<" 字节"<<endl;
	cout << "生成压缩文件: " << filename << endl;
	int len = sizeof(HEAD) + strlen(pFilename) + 1 + nSize;
	cout << len << " 字节" << endl;
	cout << "压缩比率: " << len * 1.0 / (sHead.length * 1.0) * 100 << "%"<<endl;
	return len;
}

Main.cpp
#include"Compress.h"
using namespace std;

int main() {
	cout << "==========Huffman文件压缩==========" << endl;
	char name[100];
	cout << "请输入文件名: ";
	cin >> name;
	Compress(name);
	return 0;
}
  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值