哈夫曼树
给定N个权值作为N个叶子结点
,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
定义
哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数}。树的路径长度是从树根到每一结点的路径长度之和,记为WPL = (W1*L1+W2*L2+W3*L3+…+Wn*Ln),N个权值Wi(i=1,2,…n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,…n)。可以证明哈夫曼树的WPL是最小的。
代码实现
首先定义一个哈夫曼树的结构体,其中包含四个int域和一个char域,分别为节点自身权值、双亲及左右孩子的下标和数据域。
创建哈夫曼树:
typedef struct{
char data; //数据域
int weight; //结点的权值
int parent, lChild, rChild; //双亲与孩子的下标
}htNode,*HuffmanTree;
int initHuffmanTree(HuffmanTree& HT){
HT = (htNode*)malloc(sizeof(htNode) * (2 * 10));//给HT分配2 * 10个htNOde大小的htNode类型的数组
for (int i = 1; i <= 2 * 10 - 1; i++){
HT[i].parent = HT[i].lChild = HT[i].rChild = -1;//双亲和孩子的值都置为-1
}
printf("请输入数据:\n");
for (int i = 1; i <= 10; i++){
//scanf("%c ",&HT[i].data);
char a = getchar();
if(a == '\n') //遇到回车就结束
break;
else
HT[i].data = a; //给每个结点赋予数据
}
printf("请输入权值:\n");
for (int i = 1; i <= 10; i++){
scanf("%d",&HT[i].weight);//给每个结点赋予权值
}
char c = getchar();//这个来接收上面的回车
return 1;
}
void createHuffmanTree(HuffmanTree& HT, int n){
if (n <= 1) //如果结点数小于等于1,不创建
return;
int min1, min2; //定义两个数,来存储每次选取最小两个结点的权值
int rnode, lnode;
for (int i = n + 1; i <= 2 * n -1; i++){
int min1 = INT_MAX; int lnode = -1;
int min2 = INT_MAX; int rnode = -1;
for (int j = 1; j <= i - 1; j++){
if (HT[j].weight < min1 && HT[j].parent == -1){
min2 = min1;
rnode = lnode;//碰到比min1小的,那min1的值就给第二小的min2,下标也给它
min1 = HT[j].weight; lnode = j; //然后最小的给min1,下标同理
}else if (HT[j].weight < min2 && HT[j].parent == -1){
min2 = HT[j].weight;
rnode = j;
}
}
HT[lnode].parent = HT[rnode].parent = i;
HT[i].lChild = lnode;
HT[i].rChild = rnode;
HT[i].weight = HT[lnode].weight + HT[rnode].weight;
}
}
代码实现:
void createHuffmanCode(HuffmanTree HT, HuffmanCode & HC, int n){
HC = (HuffmanCode)malloc(sizeof(HuffmanCode) * n + 1);//申请n + 1个HuffmanCode大小HuffmanCode类型的临时空间
//因为下标是从一开始,和哈夫曼树的结构对应
char* array = (char*)malloc(sizeof(char) * n);//申请n个char大小char类型的临时空间,这个临时数组记录每次遍历出来的编码
int start = 0,child = 0,parent = 0; //start为array数组记录下标,child初始为叶子结点下标,而后就是孩子结点的下标,parent记录双亲结点的下标
array[n - 1] = '\0';
for (int i = 1; i <= n; i++){//只要叶子结点的编码
start = n - 1;
child = i;
parent = HT[child].parent;
while (parent != -1){//根节点没有双亲
start--;
if (HT[parent].lChild == child) //左孩子就是0,右孩子就为1
array[start] = '0';
else
array[start] = '1';
child = parent; parent = HT[child].parent; //向根结点接近
}
HC[i] = (char*)malloc(sizeof(char) * (n - start)); //给数组里的数组申请n - start个char大小的char*类型的临时空间
strcpy(HC[i], &array[start]); //array里记录的编码给HC的第i个数组
}
free(array); //释放临时空间
}
测试结果: