哈夫曼编码的实现过程
请读者结合上一篇哈夫曼树的博客,便于理解该篇文章。简单来说,哈夫曼编码是将构造的哈夫曼树按照左孩子都标记为0 右孩子都标记为1的原则。通过此种标记的手段标记的哈弗曼树能够将编码的长度压缩到最小。本程序中默认将权值较小置为左孩子。
代码实现如下 可能不是主流思路,只是看了原理自己手写的。望指正!
文末附有测试过程
#include <stdio.h>
#include <stdlib.h>
#define MAXLEAVES 5 //初始叶子结点数
#define MAXNODE 2*MAXLEAVES - 1 //哈弗曼树的结点总数
/** 规定权值小的为左孩子,权值大的为右孩子*/
typedef struct
{
int weight; //结点权值
int parent, left, right;//父节点下标 左右孩子下标
int flag;//记录该节点是否已经判断过 没有判断过标记为0 判断过标记为1
int HuffmanCode;
}HuffNode;
void Init_HuffmanTree(HuffNode HuffArray[], int n);//初始化所有节点
void Create_HuffmanTree(HuffNode HuffArray[]);//根据下标对应权值相加 得到新的权值 并且找到双亲节点
void Show_HuffmanTree(HuffNode HuffArray[], int n);//输出哈夫曼数组
int Select_Sort(HuffNode HuffArray[], int num);//选出权值最小的两个下标
void Create_HuffmanTreeCode(HuffNode HuffArray[]); //哈弗曼编码值赋值
void Show_HuffmanTreeCode(HuffNode HuffArray[]);//输出哈弗曼编码
void Huffman_Sercet(HuffNode HuffArray[]);//任意给定权值 输出该权值的哈夫曼编码
int main()
{
//分配2n - 1 个结点内存数组
HuffNode HuffArray[MAXNODE];
printf("初始化完成的哈夫曼数组:\n");
Init_HuffmanTree(HuffArray, MAXNODE);
Show_HuffmanTree(HuffArray, MAXLEAVES);
printf("构造哈夫曼数组,并输出:\n");
Create_HuffmanTree(HuffArray);
Show_HuffmanTree(HuffArray, MAXNODE);
printf("(规定左孩子为0 右孩子为1)哈弗曼编码赋值:");
Create_HuffmanTreeCode(HuffArray);
printf("输出哈夫曼编码:\n");
Huffman_Sercet(HuffArray);
return 0;
}
/** 初始化哈夫曼数组*/
void Init_HuffmanTree(HuffNode HuffArray[], int n)
{
int i = 0;
srand(time(NULL));
for (i; i < MAXNODE; i++)//初始化
{
HuffArray[i].parent = -1;
HuffArray[i].left = -1;
HuffArray[i].right = -1;
HuffArray[i].flag = 0;
}
/*//为了方便直接给定权值
HuffArray[0].weight = 28;
HuffArray[1].weight = 10;
HuffArray[2].weight = 20;
HuffArray[3].weight = 7;
HuffArray[4].weight = 35;*/
//写完程序之后为了验证普遍性 采用随机生成的方式进行了测试
for (i = 0;i < MAXLEAVES;i++)
{
HuffArray[i].weight = rand() % 100;
}
//经过测试没有问题
}
/** 创建哈夫曼树*/
void Create_HuffmanTree(HuffNode HuffArray[])
{
int i = MAXLEAVES;
int minindex1, minindex2;
for (i; i < MAXNODE; i++)//确定需要几次大循环
{
minindex1 = Select_Sort(HuffArray, i);
minindex2 = Select_Sort(HuffArray, i);
HuffArray[i].weight = HuffArray[minindex1].weight + HuffArray[minindex2].weight;
HuffArray[i].left = minindex1;
HuffArray[i].right = minindex2;
HuffArray[minindex1].parent = HuffArray[minindex2].parent = i;
}
}
void Show_HuffmanTree(HuffNode HuffArray[], int n)
{
int i = 0;
printf("下标 权值 双亲下标 左孩子 右孩子 flag\n");
for (i;i < n;i++)
{
printf("%d %d %d %d %d %d\n", i, HuffArray[i].weight,
HuffArray[i].parent,
HuffArray[i].left,
HuffArray[i].right,
HuffArray[i].flag);
}
}
/** 输出哈弗曼编码*/
void Show_HuffmanTreeCode(HuffNode HuffArray[])
{
int i = 0;
printf("\n输出哈弗曼编码:\n");
printf("权值 哈弗曼编码\n");
for (i;i < MAXNODE;i++)
{
printf("%d %d\n", HuffArray[i].weight, HuffArray[i].HuffmanCode);
}
}
/** */
int Select_Sort(HuffNode HuffArray[], int num)
{
int m;
int minIndex;
for (m = 0;m < num;m++)
{
if (HuffArray[m].flag != 1) // 找的应该是第一个flag不为1的作为最小值
// 因为一旦第一个在某次循环中被选中 下一次也就意味着不可再被认为是minindex
{
minIndex = m;
break;
}
}
for (m = 0;m < num;m++)
{
if (HuffArray[m].flag != 1)
{
if (HuffArray[minIndex].weight > HuffArray[m].weight)
{
minIndex = m;
}
}
}
HuffArray[minIndex].flag = 1;
return minIndex;
}
/** 构造哈弗曼编码值*/
void Create_HuffmanTreeCode(HuffNode HuffArray[])
{
//从哈弗曼树数组来看 left中对应的下表其实全是左孩子 所以直接标志为0
//其次总根节点 也标记为0
//所以初始化将所有的标记为0
int i = 0, j;
for (i;i < MAXNODE;i++)
{
HuffArray[i].HuffmanCode = 0;
for (j = MAXLEAVES;j < MAXNODE;j++)
{
if (HuffArray[j].right == i)
{
HuffArray[i].HuffmanCode = 1;
}
}
}
Show_HuffmanTreeCode(HuffArray);
}
/** 任意选择权值 输出哈弗曼编码*/
void Huffman_Sercet(HuffNode HuffArray[])
{
int w = 0;
int i = 0,j = 0;
int index = 0;
int huffman[80];//哈夫曼编码的输出是反序的 用数组存一下 再倒序输出
printf("请输入选择的权值:\n");
scanf("%d", &w);
//找到权值对应的下标
for (i;i < MAXLEAVES;i++)
{
if (HuffArray[i].weight == w)
{
index = i;
break;
}
}
printf("Huffman编码:");
while (HuffArray[index].parent != -1)
{
huffman[j++] = HuffArray[index].HuffmanCode;
index = HuffArray[index].parent;
}
huffman[j] = HuffArray[index].HuffmanCode;
while (j >= 0)
{
printf("%d",huffman[j]);
j--;
}
}