哈夫曼编码问题(贪心算法)

        首先便是理解什么是前缀编码不等长编码理解数据压缩的基本方法。

        哈夫曼编码是一种十分有效的编码方法,广泛用于数据压缩中,其压缩率通常在 20%~90% 之间。采用不等长编码使编码不会有歧义也就是不二义性,并且和等长编码相比更加节省空间。

        贪心算法实现贪心算法主要思路通过每次查找最小权和次小权构建叶子节点,和作为父节点的权值,如此循环下来构成一棵完全二叉树,对应的左子树编码为0,右子树编码为1,通过遍历树即可得出最终的哈夫曼编码。

        以下是贪心算法的代码段(PS:最后有所有代码)

	int x1, x2, w1, w2;
	for (int i = 0; i < H - 1; ++i) {
		x1 = x2 = -1;
		w1 = w2 = MAXWEIGHT;
		for (int j = 0; j < H + i; ++j) {//H + i次里面遍历得到最小权重的节点
			if (haffman[j].parent == -1 && haffman[j].weight < w1) {
				w2 = w1;
				x2 = x1;
				x1 = j;
				w1 = haffman[j].weight;
			}
			else if(haffman[j].parent == -1 && haffman[j].weight < w2) {
				x2 = j;
				w2 = haffman[j].weight;
			}
		}
		haffman[H + i].leftChild = x2;
		haffman[H + i].rightChild = x1;
		haffman[H + i].weight = w1 + w2; 
		haffman[x1].parent = H + i;
		haffman[x2].parent = H + i;
	}
}

        这部分代码在循环里在嵌套一个循环,遍历得最小的两个节点。如果每次最小的更新了,那么需要把上次最小的给第二个最小的。两个循环比较复杂,难以理解,每次合成新的节点后都会更新最小的数,if语句同时需要实现复制交换的功能。

        这里体现的就是贪心算法

        主要思路通过每次查找最小权和次小权构建叶子节点,和作为父节点的权值,如此循环下来构成一棵完全二叉树,对应的左子树编码为0,右子树编码为1,通过遍历树即可得出最终的哈夫曼编码。

输出结果的示例

 用二叉数对编码进行表示(手写,有点潦草,嘻嘻)

 附上完整代码

#include <iostream>
using namespace std;
//最大字符编码数组长度
#define MAXCODELEN 100
//最大哈夫曼节点结构体数组个数
#define MAXHAFF 100
//最大哈夫曼编码结构体数组的个数
#define MAXCODE 100
#define MAXWEIGHT  10000;
typedef struct Haffman {
	int weight;	//权重
	char ch;	//字符	
	int parent;//父节点
	int leftChild;//左孩子节点
	int rightChild;//右孩子节点
} HaffmaNode;

typedef struct Code {	
	int code[MAXCODELEN];//字符的哈夫曼编码的存储
	int start;//从哪个位置开始
} HaffmaCode;
HaffmaNode haffman[MAXHAFF];
HaffmaCode code[MAXCODE];
void buildHaffman(int H) {//H是输入的需要哈夫曼编码的字符的个数 
	//哈夫曼节点的初始化之前的工作, weight为0,parent,leftChile,rightChile都为-1
	for (int i = 0; i < H * 2 - 1; ++i) {
		haffman[i].weight = 0;
		haffman[i].parent = -1;
		haffman[i].leftChild = -1;
		haffman[i].rightChild = -1;
	}
	std::cout << "请输入需要哈夫曼编码的字符和权重大小" << std::endl;
	for (int i = 0; i < H; i++) {
		std::cout << "请分别输入第" << i +1<< "个哈夫曼字符和权重" << std::endl;
		std::cin >> haffman[i].ch;
		std::cin >> haffman[i].weight;
	}
	//每次找出最小的权重的节点,生成新的节点,需要H - 1 次合并
	int x1, x2, w1, w2;
	for (int i = 0; i < H - 1; ++i) {
		x1 = x2 = -1;
		w1 = w2 = MAXWEIGHT;
		for (int j = 0; j < H + i; ++j) {//H + i次里面遍历得到最小权重的节点
			if (haffman[j].parent == -1 && haffman[j].weight < w1) {
				w2 = w1;
				x2 = x1;
				x1 = j;
				w1 = haffman[j].weight;
			}
			else if(haffman[j].parent == -1 && haffman[j].weight < w2) {
				x2 = j;
				w2 = haffman[j].weight;
			}
		}
		haffman[H + i].leftChild = x2;
		haffman[H + i].rightChild = x1;
		haffman[H + i].weight = w1 + w2; 
		haffman[x1].parent = H + i;
		haffman[x2].parent = H + i;
	}
}
void visCode(int H) {
	HaffmaCode hCode;//保存当前叶子节点的字符编码 
	int curParent;//当前父节点
	int c;//下标和叶子节点的编号
	for (int i = 0; i < H; ++i) {
		hCode.start = H - 1;
		c = i; 
		curParent = haffman[i].parent;
		while (curParent != -1) {/
			if (haffman[curParent].leftChild == c) {
				hCode.code[hCode.start] = 1;//左分支赋予0,右分支赋予1
			} else { 
				hCode.code[hCode.start] = 0;
			}
			hCode.start--;
			c = curParent;
			curParent = haffman[c].parent;
		}
		for (int j = hCode.start + 1; j < H; ++j) {//把当前的叶子节点信息保存到编码结构体里面
			code[i].code[j] = hCode.code[j];
		}
		code[i].start = hCode.start;
	}
}
int main() {
	std::cout << "请输入有多少个哈夫曼字符" << std::endl;
	int H = 0;
	std::cin >> H;
	if ( H<= 0) {
		std::cout << "您输入的个数有误" << std::endl; 
	}
	buildHaffman(H);
	visCode(H);
	for (int i = 0; i < H; ++i) {
		std::cout << haffman[i].ch << ": Haffman Code is:";//前缀码 
		for (int j = code[i].start + 1; j < H; ++j) {
			std::cout << code[i].code[j];
		}
		std::cout << std::endl;
	}return 0;	
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cramyyy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值