#include <stdio.h>
#include <malloc.h>
#include <string.h>
typedef struct{
unsigned int weight;
unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef char** HuffmanCode;
void MinTwoSelect(HuffmanTree HT,int endindex,int &s1,int &s2);
void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int n);
void PrintHuffmanCode(HuffmanCode HC,int n);
int main()
{
HuffmanTree HT;
int w[8] = {7,19,2,6,32,3,21,10};
HuffmanCode HC;
HuffmanCoding(HT,HC,w,8);
PrintHuffmanCode(HC,8);
return 0;
}
void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int n)
{
if(n<=1) return; //如果只有1个字符需要编码,则无需操作
int m = 2 * n - 1; //n个字符(叶子节点也为n个)需要2n-1个存储空间
HT = (HuffmanTree) malloc ( ( m + 1 ) * sizeof(HTNode)); //0号空间不用
//初始化HT,前n个节点保存Huffman树权重信息,后面直接初始化为0
for(int i = 1;i<=n;++i,++w)
{
HT[i].weight = *w;
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
for(int i=n+1;i<=m;i++)
{
HT[i].weight = 0;
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
//构造Huffman树
//在HT[1]至HT[i-1]选择parent为0且weight最小的两个节点,其序号分别为S1和S2
int s1,s2;
for(int i=n+1; i<=m;i++)
{
MinTwoSelect(HT,i-1,s1,s2);
HT[s1].parent = i;
HT[s2].parent = i;
HT[i].lchild = s2;
HT[i].rchild = s1;
HT[i].weight = HT[s1].weight + HT[s2].weight;
}
/*
//从叶子节点到根逆向求每个字符的Huffman编码
HC = (HuffmanCode)malloc((n+1)*sizeof(char*));//分配n个字符编码的头指针向量
char* cd = (char*)malloc(n*sizeof(char)); //分配求编码的工作空间
cd[n-1] = '\0' ;//编码结束符号
int start;
for(int i = 1;i<= n;i++) //逐个字符求编码
{
start = n-1; //编码结束符的位置
for(int c = i,f = HT[i].parent; f!=0; c = f, f = HT[f].parent) //从叶子节点到根节点逆向求编码
{
if(HT[f].lchild == c)
{
cd[--start] = '1';
}
else
{
cd[--start] = '0';
}
}
HC[i] = (char*)malloc((n-start)*sizeof(char)); //为第i个字符编码分配空间
strcpy(HC[i],&cd[start]);
}
free(cd);
*/
//无栈非递归遍历Huffman树,求Huffman编码
HC = (HuffmanCode)malloc((n+1)*sizeof(char*));
char* cd = (char*)malloc(n*sizeof(char)); //分配求编码的工作空间
//cd[n-1] = '\0' ;//编码结束符号
int p = m,cdlen = 0;
for(int i = 1; i<= m; ++i)
{
HT[i].weight = 0; //用于遍历Huffman树时保存节点状态标志
}
while(p)
{
if(HT[p].weight == 0) //向左
{
HT[p].weight = 1;
if(HT[p].lchild != 0)
{
p = HT[p].lchild;
cd[cdlen] = '0';
cdlen++;
}
else if(HT[p].rchild == 0) //登记叶子节点的字符编码
{
HC[p] = (char*) malloc ((cdlen+1)*sizeof(char));
cd[cdlen] = '\0';
strcpy(HC[p],cd); //复制编码串
}
}
else if(HT[p].weight == 1) //向右
{
HT[p].weight = 2;
if(HT[p].rchild != 0)
{
p = HT[p].rchild;
cd[cdlen] = '1';
cdlen++;
}
}
else
{ //HT[p].weight == 2 代表退回
HT[p].weight = 0; //退回到父节点,编码长度减1
p = HT[p].parent;
--cdlen;
}
}
}
void MinTwoSelect(HuffmanTree HT,int endindex,int &s1,int &s2)
{
HuffmanTree p = HT;
HT[0].weight = 10000; //初始化第一个空间为“权重和”不可能到达的值
s1 = s2 = 0; //s1保存第二小数据,s2保存第一小数据
for(int i =1; i <= endindex; i++)
{
if(HT[i].parent != 0 || HT[i].weight > HT[s1].weight) //如果有父节点,则代表已经处理过,则跳过
continue;
if(HT[i].weight < HT[s1].weight)
{
s1 = i;
}
if(HT[s1].weight < HT[s2].weight)
{
int temp = s1;
s1 = s2;
s2 = temp;
}
}
}
void PrintHuffmanCode(HuffmanCode HC,int n)
{
for(int i=1;i<=n;i++)
{
printf("第%d个字符的编码为:%s\n",i,HC[i]);
}
}