感觉这一周在写huffman树的时候,碰到了几个以前没有熟记的知识点,所以就写下这篇文章就当是笔记啦.
- 数据结构
二叉树 :书中自定义了一个结构体
typedef struct
{
unsigned int weight;
unsigned int parent, lchild, rchild;
}HTNode,*HuffmanTree; //动态分配数组存储哈夫曼树
在这里,个人觉得还可以插入一个定义对应字符的ch变量,这样就没必要再单独设置一个字符数组类型的变量。
- 核心实现函数
void InitHuffmanTree(HuffmanTree *HT, HuffmanCode &HC, int *w, int n)
//w存放n个字符的权值(均>0),构造哈夫曼树HT,并求出n个字符的哈夫曼编码HC。
{
if (n <= 1)
return;
int m ; //用来表示结点个数
int s1 ,s2; //用来当作权值最小的两个结点的下标
HuffmanTree p; //用来进行指针的移动
m = 2 * n - 1;
*HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode)); //0号单元未用
p = *HT + 1;
(*HT)[0].weight = 0;
(*HT)[0].parent = 0;
(*HT)[0].lchild = 0;
(*HT)[0].rchild = 0;
int i;
for (i=1;i <= n;i++, p++, w++) // 从1号单元开始初始化
{
(*p).weight = *w;
(*p).parent = 0;
(*p).lchild = 0;
(*p).rchild = 0;
}
for (;i <= m;i++, p++) //空树
{
(*p).weight = 0;
(*p).parent = 0;
(*p).lchild = 0;
(*p).rchild = 0;
}
for (i = n + 1;i <= m;++i) //建立哈夫曼树
{
select(*HT, i - 1, &s1, &s2);
(*HT)[s1].parent = i;
(*HT)[s2].parent = i;
(*HT)[i].lchild = s1;
(*HT)[i].rchild = s2;
(*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight;
}
}
void select(HuffmanTree HT, int n, int *s1, int *s2)
{
unsigned int a = 65535, b = 65535; //表示s1,s2的权值
int temp, i;
*s1 = 0; *s2 = 0; //对s1,s2初始化
for (i = 1;i <= n;i++)
{
if (HT[i].parent)
continue;
if (HT[i].weight < a)
{
b = a;
a = HT[i].weight;
*s2 = *s1;
*s1 = i;
}
else if (HT[i].weight < b)
{
b= HT[i].weight;
*s2 = i;
}
}
if (a > b) //保证s1对应的权值小于s2对应的权值
{
temp = *s2;
*s2 = *s1;
*s1 = temp;
}
}
该函数的思路是0号单位不用,在1号到n号进行权值的录入,这些单位的双亲和左右孩子都是0。(即没有)而对应字符可以在第0位开始。之后从n+1位到2*n-1位进行哈夫曼树的构建。构建的想法是,在HT[1…i-1]里选择双亲为0,且权值最小的两个单位,其序号分别对应s1和s2,在把他们构造一个新的二叉树,直到i=2n-1。
- 如果只有一个或者没有字符,则没有编码的必要,直接return。注意,这里是void类型的函数,return 后面不加东西,表示退出函数。
- 设置一个指针p来进行对数据的录入。其实只用*HT也是可以的,但每次进行指针的操作之后,就得进行HT–的循环来回到第一个存储单位,时间复杂度就会大大增加。
- 在设置一个指针时,先让指针指向NULL,再将指针赋予其他值。