一、哈夫曼树
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的三个节点为例)