6.2Huffman

Huffman代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef double DataType;//节点权值的数据类型

typedef struct HTNode {//单个节点的信息
	DataType weight;//权值 
	int parent;//父节点 
	int lc, rc;//左右孩子 
} *HuffmanTree;

typedef char **HuffmanCode;//字符指针数组中存储的元素类型

//在下标为 1 到 i - 1的范围找到权值最小的两个值的下标,其中s1的权值小于s2的权值
void Select(HuffmanTree& HT, int n, int& s1, int& s2) {
	int min, i;
	//找第一个最小值
	for(i = 1; i <= n; i++) {
		if(HT[i].parent == 0) {
			min = i;
			break;
		}
	}
	for(i = min + 1; i <= n; i++) {
		if(HT[i].parent == 0 && HT[i].weight < HT[min].weight) {
			min = i;
		}
	}
	s1 = min;//第一个最小值给s1
	//找第二个最小值
	for(i = 1; i <= n; i++) {
		if(HT[i].parent == 0 && i != s1) {
			min = i;
			break;
		}
	} 
	for(i = min + 1; i <= n; i++) {
		if(HT[i].parent == 0 && HT[i].weight < HT[min].weight && i != s1) {
			min = i;
		}
	}
	s2 = min;//第二个最小值给s2 
} 

//构建哈夫曼树
void CreateHuff(HuffmanTree& HT, DataType* w, int n) {
	int m = 2 * n - 1;//哈夫曼树总结点数
	HT = (HuffmanTree)malloc(sizeof(HTNode) * (m + 1));//开 m+1 个HTNode 因为下标为0的HTNode不存储数据  
	int i;
	for(i = 1; i <= n; i++) {
		HT[i].weight = w[i - 1];//赋权值给n个叶子节点 
	} 
	for(i = n + 1; i <= m; i++) {//构建哈夫曼树
		//选择权值最小的s1和s2 生成他们的父节点 
		int s1, s2;
		Select(HT, i - 1, s1, s2);//在下标为1 到 i - 1的范围找到权值最小的两个值的下标 其中s1的权值小于s2的权值
		HT[i].weight = HT[s1].weight + HT[s2].weight;//i 的权重是s1和s2的权重之和
		HT[s1].parent = i;//s1的父亲是i 
		HT[s2].parent = i;//s2的父亲是i
		HT[i].lc = s1;//左孩子是s1
		HT[i].rc = s2;//右孩子是s2
	} 
	//打印哈夫曼树中各节点之间的关系
	printf("哈夫曼树为:>\n");
	printf("下标   权值   父节点   左孩子   右孩子\n");
	printf("0                                     \n");
	for(i = 1; i <= m; i++) {
		printf("%-4d   %-6.2lf   %-6d   %-6d   %-6d\n",i, HT[i].weight, HT[i].parent, HT[i].lc, HT[i].rc);
	} 
	printf("\n");
} 

//生成哈夫曼代码
void HuffCoding(HuffmanTree& HT, HuffmanCode& HC, int n) {
	HC = (HuffmanCode)malloc(sizeof(char*) * (n + 1));//开n + 1个空间 因为下标为0的空间不用
	char* code = (char*)malloc(sizeof(char) * n);//辅助空间 编码最长为n(最长时,前n - 1个用于存储数据,最后一个用于存放'\0')
	code[n - 1] = '\0';//辅助空间最后一个位置为'\0'
	int i;
	for(i = 1; i <= n; i++) {
		int start = n - 1;//每次生成数据的哈夫曼编码之前 先将start指针指向'\0' 
		int c = i;//正在进行的第i个数据的编码 
		int p = HT[c].parent;//找到该数据的父节点 
		while(p) {//直到父节点为0 
			if(HT[p].lc == c) {//如果该节点是其父节点的左孩子 则编码为0 否则为1 
				code[--start] = '0';
			} else {
				code[--start] = '1';
			}
			c = p;//继续向上进行编码 
			p = HT[c].parent;//c的父节点 
		}
		HC[i] = (char*)malloc(sizeof(char) * (n - start));//开辟用于存储编码的内存空间 
		strcpy(HC[i], &code[start]);//将编码拷贝到字符指针数组中的相应位置 
	} 
	free(code);//释放辅助空间 
} 

//主函数
int main() {
	int n = 0, i;
	printf("请输入数据个数:>");
	scanf("%d", &n);
	DataType* w = (DataType*)malloc(sizeof(DataType) * n);
	if(w == NULL) {
		printf("malloc fail\n");
		exit(-1);
	} 
	printf("请输入数据:>");
	for(i = 0; i < n; i++) {
		scanf("%lf", &w[i]);
	}
	HuffmanTree HT;
	CreateHuff(HT, w, n);//创建哈夫曼树
	
	HuffmanCode HC;
	HuffCoding(HT, HC, n);//构建哈夫曼编码
	
	for(i = 1; i <= n; i++) {
		printf("数据%.2lf的编码为:%s\n", HT[i].weight, HC[i]);
	} 
	free(w);
	return 0;
} 
运行结果
请输入数据个数:>8
请输入数据:>0.05 0.29 0.07 0.08 0.14 0.23 0.03 0.11
哈夫曼树为:>
下标   权值   父节点   左孩子   右孩子
0
1      0.05     9        0        0
2      0.29     14       0        0
3      0.07     10       0        0
4      0.08     10       0        0
5      0.14     12       0        0
6      0.23     13       0        0
7      0.03     9        0        0
8      0.11     11       0        0
9      0.08     11       7        1
10     0.15     12       3        4
11     0.19     13       9        8
12     0.29     14       5        10
13     0.42     15       11       6
14     0.58     15       2        12
15     1.00     0        13       14

数据0.05的编码为:0001
数据0.29的编码为:10
数据0.07的编码为:1110
数据0.08的编码为:1111
数据0.14的编码为:110
数据0.23的编码为:01
数据0.03的编码为:0000
数据0.11的编码为:001
总结
  • 哈夫曼树不存在度为1的节点
  • 若给定n个数要求构造哈夫曼树,则构建出来的哈夫曼树的节点总数为2n-1,度为0的叶子节点个数一定比度为2的节点个数多1
  • 构建哈夫曼树就是反复选择两个最小的元素进行合并,直到只剩下一个元素为止
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值