#include<stdio.h>
#include<Windows.h>
typedef struct
{
int weight;
int lchild, rchild, parent;
}HFTNode;
typedef HFTNode HFMT[100]; //HFMT类型为有100个HFNode
int n;
void initHFMT(HFMT T)//初始化
{
int i;
printf("输入权值个数:\n");
scanf("%d", &n);
for (i = 0; i < 2 * n - 1; i++)//n个叶子节点的哈夫曼树公有2n-1个节点
{
T[i].weight = 0;
T[i].lchild = -1;
T[i].rchild = -1;
T[i].parent = -1;
}
}
void InputWeight(HFMT T)//输入权值
{
int w, i;
for (i = 0; i < n; i++)
{
printf("输入第%d个权值", i + 1);
scanf("%d", &w);
T[i].weight = w;
}
}
void SelectMin(HFMT T, int i, int *p1, int *p2)//找出两个最小的节点
{
long min1 = 99999;
long min2 = 99999;
int j;
for (j = 0; j <= i; j++)
{
if (T[j].parent == -1)
{
if (min1 > T[j].weight)
{
min1 = T[j].weight;
*p1 = j; //通过p1带回序列号
}
}
}
for (j = 0; j <= i; j++)
{
if (T[j].parent == -1)
{
if (min2 > T[j].weight&&j != (*p1))
{
min2 = T[j].weight;
*p2 = j;
}
}
}
}
void CreateHFMT(HFMT T)
{
int i, p1, p2;
initHFMT(T);
InputWeight(T);
for (i = n; i < 2 * n - 1; i++)
{
SelectMin(T, i - 1, &p1, &p2);
T[p1].parent = T[p2].parent = i;
T[i].lchild = T[p1].weight;
T[i].rchild = T[p2].weight;
T[i].weight = T[p1].weight + T[p2].weight;
}
}
void PrintHFMT(HFMT T)
{
printf("\n\n哈夫曼树各边显示:");
int i, k = 0;
for (i = 0; i < 2 * n - 1; i++)
{
if (T[i].lchild != -1)
{
if (!(k % 2))
{
printf("\n");
}
printf("\t\t(%d,%d),(%d,%d)", T[i].weight, T[i].lchild, T[i].weight, T[i].rchild);
k++;
}
}
printf("\n\n");
}
void hfnode(HFMT T, int i, int j)
{
//由于在生成编码时是从下往上找的,所以在输出时需要倒序输出,类似于压栈与出栈
int stack[100] = { 0 };
int top = -1;
while (T[i].parent != -1)
{
top++;
j = T[i].parent;
if (T[j].rchild == T[i].weight)
stack[top] = 1;
else
stack[top] = 0;
i = j;
}
for (; top != -1; top--)
printf("%d", stack[top]);
}
void HuffmanNode(HFMT T)
{
printf("哈夫曼编码为: \n");
int i, j, k = 0;
for (i = 0; i < n; i++)
{
j = 0;
if (k % 2 == 0)
printf("\n");
printf("\t\t%d: ", T[i].weight);
k++;
hfnode(T, i, j);
}
}
int main(void)
{
HFMT HT;
CreateHFMT(HT);
PrintHFMT(HT);
HuffmanNode(HT);
printf("\n");
system("pause");
}
哈夫曼编码可用于压缩时使用,将频率较高的字符01组成的字符串长度越短,频率较低的长度越长,以保证整体的01组合长度最短。
建立过程:
1.统计各字符的个数,根据个数来建立哈夫曼树。
2.从根节点到目标节点,每经过一个节点就要多一个0或1,如果在左边,就取0,右边取1
3.将每个节点对应的01码组合成一串哈夫曼编码
解析哈夫曼编码:
由于哈夫曼编码有一个特性,就是每一个字符的哈夫曼编码的开头都不能与其他等同于另一个字符的编码。如一个字符编码为010110 ,另一个字符为010,这就是不可以的,不然取出010后,110就不认识了。所以通过这个特点,我采用的方法是遍历这个01串,先建立一个空字符串,每次赋值一个字符到这个字符串后面,然后进行对比,如果遇到匹配的,就解析出了对应的字符,并把字符串置为空,用来继续遍历后面的,如果没有匹配,就继续添加,直到匹配到一个为止。