关于哈夫曼树的介绍,网上的资料很多,这里就不多介绍了。下面是C语言的代码实现。GCC5.3.0编译通过。
// Created by Jacky on 2017-5-18
// main.c -- 哈夫曼树(数组表示)
#include <stdio.h>
#include <stdlib.h>
/**
* 哈夫曼树的结点定义
*/
typedef struct HuffmanNode
{
char data; // 字符
int weight; // 权值,字符出现次数
int parent; // 父结点的数组下标
int left; // 左孩子的数组下标
int right; // 右孩子的数组下标
} HuffmanNode;
/**
* 哈夫曼树定义
*/
typedef struct HuffmanTree
{
int count; // 结点数,也就是数组长度 = 2 * 叶子结点 - 1
HuffmanNode *nodes; // 结点数组
} HuffmanTree;
#define HUFFMANTREE_INIT {0, NULL}
void InitHuffmanTree(HuffmanTree *T, const char *str);
void CreateHuffmanTree(HuffmanTree *T);
void PrintHuffmanTree(HuffmanTree T);
void Visit(HuffmanNode *node);
void PreOrderTraversal(HuffmanTree T, void (*visit)(HuffmanNode *));
void InOrderTraversal(HuffmanTree T, void (*visit)(HuffmanNode *));
void PostOrderTraversal(HuffmanTree T, void (*visit)(HuffmanNode *));
int main(int argc, char const *argv[])
{
HuffmanTree T = HUFFMANTREE_INIT;
InitHuffmanTree(&T, "aaaabbc");
CreateHuffmanTree(&T);
PrintHuffmanTree(T);
// PostOrderTraversal(T, Visit);
free(T.nodes);// 释放结点申请的内存
T.nodes = NULL;
return 0;
}
/**
* 初始化哈夫曼树。
* 根据给定的字符串,统计每个字符出现的次数。
* 并用统计的信息来构造哈夫曼树的叶子结点。
* @param T 哈夫曼树对象指针
* @param str 需要统计的字符串
*/
void InitHuffmanTree(HuffmanTree *T, const char *str)
{
// 统计每个字符出现的次数。
int arr[256] = {0};
while (*str)
{
arr[*str]++;
str++;
}
int leafCount = 0;
// 统计叶子结点的个数。
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
{
if (arr[i])
{
leafCount++;
// printf("%c %d\n", i, arr[i]);
}
}
// printf("leafCount = %d.\n", leafCount);
HuffmanNode *nodes = (HuffmanNode *)malloc((2 * leafCount - 1) * sizeof(HuffmanNode));
T->count = 2 * leafCount - 1;
leafCount = 0;
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
{
if (arr[i])
{
nodes[leafCount].data = i;
nodes[leafCount].weight = arr[i];
nodes[leafCount].parent = nodes[leafCount].left = nodes[leafCount].right = -1;
leafCount++;
}
}
T->nodes = nodes;
}
/**
* 通过给定的叶子结点构造整棵哈夫曼树。
* @param T 哈夫曼树对象指针
*/
void CreateHuffmanTree(HuffmanTree *T)
{
int leafCount = (T->count + 1) / 2;// 结点数 = 2 * 叶子结点 - 1
for (int i = leafCount; i < T->count; ++i)
{
int min1 = -1;// weight最小的结点数组下标
int min2 = -1;// weight次小的结点数组下标
for (int j = 0; j < i; ++j)
{
if (T->nodes[j].parent == -1)
{
if (min1 == -1)// 第一次进入的时候,给min1赋值
{
min1 = j;
}
else if (min2 == -1)// 第二次进入的时候,给min1,min2赋值
{
if (T->nodes[j].weight < T->nodes[min1].weight)
{
min2 = min1;
min1 = j;
}
else
{
min2 = j;
}
}
else// 第三次及之后进入
{
if (T->nodes[j].weight < T->nodes[min1].weight)
{
min2 = min1;
min1 = j;
}
else if (T->nodes[j].weight < T->nodes[min2].weight)
{
min2 = j;
}
}
}
}
// printf("min1 = %d, min2 = %d.\n", min1, min2);
T->nodes[i].data = 0;
T->nodes[i].weight = T->nodes[min1].weight + T->nodes[min2].weight;
T->nodes[i].parent = -1;
T->nodes[i].left = min1;
T->nodes[i].right = min2;
T->nodes[min1].parent = T->nodes[min2].parent = i;
}
}
/**
* 哈夫曼树的结点的访问
* @param node 结点地址
*/
void Visit(HuffmanNode *node)
{
printf("%c %2d\n", node->data, node->weight);
}
void PreOrder(HuffmanTree T, void (*visit)(HuffmanNode *), int index)
{
if (index != -1)
{
visit(&T.nodes[index]);
PreOrder(T, visit, T.nodes[index].left);
PreOrder(T, visit, T.nodes[index].right);
}
}
void InOrder(HuffmanTree T, void (*visit)(HuffmanNode *), int index)
{
if (index != -1)
{
InOrder(T, visit, T.nodes[index].left);
visit(&T.nodes[index]);
InOrder(T, visit, T.nodes[index].right);
}
}
void PostOrder(HuffmanTree T, void (*visit)(HuffmanNode *), int index)
{
if (index != -1)
{
PostOrder(T, visit, T.nodes[index].left);
visit(&T.nodes[index]);
PostOrder(T, visit, T.nodes[index].right);
}
}
/**
* 前序遍历
*/
void PreOrderTraversal(HuffmanTree T, void (*visit)(HuffmanNode *))
{
PreOrder(T, visit, T.count - 1);
}
/**
* 中序遍历
*/
void InOrderTraversal(HuffmanTree T, void (*visit)(HuffmanNode *))
{
InOrder(T, visit, T.count - 1);
}
/**
* 后续遍历
*/
void PostOrderTraversal(HuffmanTree T, void (*visit)(HuffmanNode *))
{
PostOrder(T, visit, T.count - 1);
}
/**
* 打印哈夫曼树信息,方便调试
* @param T 哈夫曼树对象
*/
void PrintHuffmanTree(HuffmanTree T)
{
printf("\ncount = %d.\n", T.count);
char *rows[] = {"index", "data", "weight", "parent", "left", "right"};
int count = T.count;
for (int i = 0; i < sizeof(rows) / sizeof(rows[0]); ++i)
{
if (i == 0)
{
printf("+--------+");
for (int i = 0; i < count; ++i)
{
printf("----+");
}
}
printf("\n|%-8s|", rows[i]);
for (int j = 0; j < count; ++j)
{
switch (i)
{
case 0:// index
printf(" %-3d|", j);
break;
case 1:// data
if (T.nodes[j].data == 10)// 回车
{
printf(" %-3d|", T.nodes[j].data);
}
else
{
printf(" %-3c|", T.nodes[j].data);
}
break;
case 2:// weight
printf(" %-3d|", T.nodes[j].weight);
break;
case 3:// parent
printf(" %-3d|", T.nodes[j].parent);
break;
case 4:// left
printf(" %-3d|", T.nodes[j].left);
break;
case 5:// right
printf(" %-3d|", T.nodes[j].right);
break;
}
}
printf("\n+--------+");
for (int j = 0; j < count; ++j)
{
printf("----+");
}
}
printf("\n");
}