哈夫曼树
哈夫曼树(Huffman Tree)是一种用于压缩数据的数据结构。哈夫曼树是一种二叉树,它的每个节点都有一个权重,表示该节点在编码中出现的频率。哈夫曼树的构建过程如下:
- 将所有节点按照权重从小到大排序。
- 选择权重最小的两个节点,将它们合并成一个新节点,新节点的权重等于这两个节点的权重之和。
- 将新节点加入排序后的节点列表中。
- 重复步骤2和3,直到只剩下一个节点
图解
代码实现
#include "LHuffTree.h"
#include "LSort.h"
#include <math.h>
#include <string.h>
int compareHuffNode(const void *a, const void *b){
int temp = ((LHuffNode*)a)->weight-((LHuffNode*)b)->weight;
return temp==0?0:-temp/abs(temp);
}
//创建哈夫曼树(通过字典创建HUFFMAN树)
LHuffTree *createHuffmanTree(LDictionary *dict){
LHuffTree *tree = (LHuffTree*)malloc(sizeof(LHuffTree));
tree->size = dict->size;
tree->nodecount = tree->size * 2 - 1;
tree->height = 0;
tree->codes = NULL;
tree->nodes = (LHuffNode*)malloc(sizeof(LHuffNode) * tree->nodecount);
//将字典中的键值对转为森林
for(int i = 0; i < dict->size; i++){
tree->nodes[i].weight = dict->arr[i].value;
tree->nodes[i].ch = dict->arr[i].key;
tree->nodes[i].parent = -1;
tree->nodes[i].left = -1;
tree->nodes[i].right = -1;
}
//跟据weight对所有树的根节点排序
quickSort(tree->nodes, tree->size, sizeof(LHuffNode), compareHuffNode);
//不断取两颗根节点权重最小的树构成新的树,直到森林只剩一棵树
for(int i = 0; tree->size-2-i>=0; i++){
memcpy(&tree->nodes[tree->nodecount-2*(i+1)], &tree->nodes[tree->size-2-i], 2*sizeof(LHuffNode));
int newnode_weight = tree->nodes[tree->nodecount-2*(i+1)].weight + tree->nodes[tree->nodecount-2*(i+1)+1].weight;
int j;
for(j=tree->size-3-i; j>=0&&tree->nodes[j].weight<newnode_weight ; j--);
if(j!=tree->size-3-i)
memcpy(&tree->nodes[j+2], &tree->nodes[j+1], sizeof(LHuffNode)*((tree->size-3-i)-j));
tree->nodes[j+1].weight = newnode_weight;
tree->nodes[j+1].left = tree->nodecount-(2*(i+1));
tree->nodes[j+1].right = tree->nodecount-(2*(i+1))+1;
tree->nodes[j+1].ch = 0;
}
//连接所有节点的父节点
for(int i = 0; i < tree->nodecount; i++){
if(tree->nodes[i].left!=-1)
tree->nodes[tree->nodes[i].left].parent = i;
if(tree->nodes[i].right!=-1)
tree->nodes[tree->nodes[i].right].parent = i;
}
//计算哈弗曼树的高度
int height = 1;
for(int curindex = tree->nodecount-1;tree->nodes[curindex].parent!=-1;height++,curindex=tree->nodes[curindex].parent);
tree->height = height;
// printf("height:%d\n", height);
// for(int i = 0; i < tree->nodecount; i++)
// printf("%d: left:%d, right:%d, parent:%d, ch:%c\n", i, tree->nodes[i].left, tree->nodes[i].right, tree->nodes[i].parent, tree->nodes[i].ch);
// printf("\n");
return tree;
}
//跟据哈夫曼树生成哈夫曼编码
LHuffCode* createHuffmanCode(LHuffTree *tree){
LHuffCode *huffcodes = (LHuffCode*)malloc(sizeof(LHuffCode)*tree->size);
memset(huffcodes, 0, sizeof(LHuffCode)*tree->size);
char*temparr = (char*)malloc(sizeof(char)*tree->height);
int templen;
int curcode_num=0;
for(int i = 1; i < tree->nodecount; i++){
memset(temparr, 0, sizeof(char)*tree->height);
templen = 0;
huffcodes[curcode_num].code = (char*)malloc(sizeof(char)*tree->height);
memset(huffcodes[curcode_num].code, 0, sizeof(char)*tree->height);
if(tree->nodes[i].ch == 0)
continue;
//从叶子节点开始向上遍历,生成编码
for(int curindex = i; tree->nodes[curindex].parent!=-1; curindex=tree->nodes[curindex].parent){
int parent = tree->nodes[curindex].parent;
if(tree->nodes[parent].left == curindex){
temparr[templen++] = '1';
}else{
temparr[templen++] = '2';
}
}
//反转编码,并保存
for(int j = 0; j < templen; j++){
huffcodes[curcode_num].code[j] = temparr[templen-1-j];
}
huffcodes[curcode_num].ch = tree->nodes[i].ch;
curcode_num++;
}
tree->codes=huffcodes;
return huffcodes;
}
//释放哈夫曼树
void freeLHuffmanTree(LHuffTree *tree){
if(tree->nodes != NULL)
free(tree->nodes);
if(tree->codes != NULL){
for(int i = 0; i < tree->size; i++){
if(tree->codes[i].code != NULL)
free(tree->codes[i].code);
}
free(tree->nodes);
}
free(tree);
}
#ifndef _LHUFFTREE_H_
#define _LHUFFTREE_H_
#include "LDictionay.h"
struct _HuffNode
{
char ch;
int weight;
int parent,left,right;
};
typedef struct _HuffNode LHuffNode;
struct _HuffCode
{
char ch;
char *code;
};
typedef struct _HuffCode LHuffCode;
struct _HuffTree
{
LHuffNode *nodes;
LHuffCode *codes;
int size;
int nodecount;
int height;
};
typedef struct _HuffTree LHuffTree;
//创建哈夫曼树(通过字典创建HUFFMAN树)
LHuffTree *createHuffmanTree(LDictionary *dict);
//跟据哈夫曼树生成哈夫曼编码
LHuffCode* createHuffmanCode(LHuffTree *tree);
//释放哈夫曼树
void freeLHuffmanTree(LHuffTree *tree);
#endif /* _LHUFFTREE_H_ */