哈夫曼树(最优二叉树)

一、哈夫曼树

1.1 所有叶子元素的权数*深度(可以理解为路径长)的和最小的树,所以被称为最优二叉树

1.2 应用点——最短前缀码

问题1. 为什么哈夫曼编码能保证是前缀码?
因为没有一片树叶是另一片树叶的祖先,所以每个叶子节点的编码就不可能是其他结点的前缀。
问题2. 为什么哈夫曼编码能保证字符编码总长最短?
因为哈夫曼数的带权路径最短(最优二叉树),故字符编码总长最短。

二、代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define Max 1e9 //定义最大值,为后续找两小做铺垫

int n;//节点个数
typedef struct {
	char name[10];//定义标识符
	int weight;//权值
	int parent, lchild, rchild;
}HT;

//哈夫曼树有2*n-1个节点;其中要合并n-1次,所以要产生n-1节点,然后叶子节点有n个,所以有2*n-1
void init(HT** ht, char*** hcode) {
	printf("请输入n(n>1):");
	scanf("%d", &n);
	getchar();
	*ht = (HT*)malloc(sizeof(HT) * 2 * n);//从下标1开始,所以定义2*n个空间
	*hcode = (char**)malloc(sizeof(char*) * (n + 1));//从下标1开始,所以定义n+1个空间
	for (int i = 1; i < 2 * n; i++) {
		if (i <= n){
			printf("请输入结点标识name和节点权值weight:");
			scanf("%s %d", &((*ht)[i].name), &((*ht)[i].weight));
		}
		(*ht)[i].parent = (*ht)[i].lchild = (*ht)[i].rchild = 0;
	}
	printf("初始化完成!");
}

void selectNums(HT* ht,int* min1, int* min2,int end) {
	for (int i = 1; i < end; i++) {
		if (!ht[i].parent) {
			if (ht[i].weight < ht[*min1].weight) {
				*min2 = *min1;
				*min1 = i;
			}
			else if (ht[i].weight <= ht[*min2].weight) *min2 = i;
		}
	}
}

void createHT(HT* ht) {
	for (int i = n + 1; i < 2 * n; i++) {
		int min1 = i, min2 = i;
		ht[i].weight = Max;
		selectNums(ht, &min1, &min2, i);//找两小
		//合两小
		ht[i].weight = ht[min1].weight + ht[min2].weight;
		ht[i].lchild = min1; 
		ht[i].rchild = min2;
		ht[min1].parent = i;
		ht[min2].parent = i;
	}
}

void createHCode(HT* ht, char** hcode) {
	char* code = (char*)malloc(sizeof(char) * n);//辅助空间
	code[n - 1] = '\0';
	for (int i = 1; i <= n; i++) {
		int start = n - 1;
		int child = i;
		int parent = ht[child].parent;
		while (parent) {
			if (ht[parent].lchild == child) code[--start] = '0';
			else code[--start] = '1';
			child = parent;
			parent = ht[child].parent;
		}
		hcode[i] = (char*)malloc(sizeof(char) * (n - start));
		strcpy(hcode[i], &code[start]);
	}
}

void output(HT* ht, char** hcode) {
	for (int i = 1; i <= n; i++) 
		printf("\n%s:%s", ht[i].name, hcode[i]);
	printf("\n输出完毕!");
}

int main() {
	HT* ht = NULL;
	char** hcode = NULL;
	init(&ht, &hcode);
	createHT(ht);
	createHCode(ht, hcode);
	output(ht, hcode);
	return 0;
}

二、结果(以权值为1 2 3的三个节点为例)

posted @ 2023-09-27 22:22  彭乐祥  阅读(94)  评论(0编辑  收藏  举报