#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 27 //带权值的叶子节点个数或者是需要编码的字符数
#define M 2*N-1//n个叶子节点构造的哈弗曼树有2n-1个结点(因为该树只有叶子和度为2的结点)
#define MAX 10000
typedef struct
{
unsigned int weight; //权值只能是正数
unsigned int parent, lchild, rchild;
}HTNode, *HuffmanTree;
typedef char **HuffmanCode;//存储每个字符的哈夫曼编码表 是一个字符指针数组 每个数组元素是指向字符指针的指针
void Select(HuffmanTree HT, int k, int &s1, int &s2);
void PrintTree(HuffmanTree HT, char ch[]);
void HuffmanCoding(HuffmanTree &HT, int *w, int n);
void PrintCode(HuffmanCode HC, char ch[]);
void CreatTree(HuffmanTree &HT, int *w, int n);
void getCode1(HuffmanTree HT, HuffmanCode &HC);
void decodeHuffmanTree(HuffmanTree HT, HuffmanCode, char *ch, int len);
void getCode(HuffmanCode &code, char *ch, char *words, HuffmanCode &HC);
int main ()
{
HuffmanTree HT;char *codes;
int m=0;
HuffmanCode HC, HC1;//HC有n个元素 每个元素是一个指向字符串的指针 即每个元素是一个char *的变量
char ch[N+1]={‘0′,’ ‘, ‘a’, ‘b’, ‘c’, ‘d’,’e’,’f’,’g’,’h’,’i’,’j’,’k’,’l’,’m’,’n’,’o’,’p’,’q’,’r’,’s’,’t’,’u’,’v’,’w’,’x’,’y’,’z’};//每个元素与下面的权值相对应 第一个元素不使用留空
int w[N+1]={ 0, 186, 64, 13, 22, 32,103, 21, 15, 47, 57, 1, 5, 32, 20, 57, 63, 15, 1, 48, 51,80,23,8,18,1,16};
char *word=”0this program is my favorite”;
HuffmanCode code;
HuffmanCoding(HT, w, N);//建立哈弗曼树并且求出哈弗曼编码
getCode1(HT,HC);
PrintTree(HT,ch);
PrintCode(HC, ch);
getCode(code, ch, word, HC);//子串的编码 原串 子串 原串的哈弗曼编码
PrintCode(code, word);
decodeHuffmanTree(HT, code, ch, strlen(word));
}
void HuffmanCoding(HuffmanTree &HT, int *w, int n)
{//首先构造哈弗曼树
int i, s1, s2;
if(n<=1)
return;
HT=(HuffmanTree)malloc((M+1)*sizeof(HTNode));//0号单元不使用 而树中有n个结点所以要+1
for(i=1; i<=N; i++)
{
HT[i]={w[i], 0, 0, 0};//HT的前n个分量存储叶子节点 均带有权值
}
for(;i<=M; ++i) //HT后m-n个分量存储中间结点 最后一个分量是整棵树的根节点
{
HT[i]={0, 0, 0, 0};
}
for(i=N+1; i<=M; i++)//开始构建哈弗曼树 即创建HT的后m-n个节点的过程,直到创建出根节点 使用哈夫曼算法
{//i的初始值为N+1 树的前N个位置均有数了
Select(HT, i-1, s1, s2);//在HT[1……i-1]中选择parent为0且权值最小的两个结点, 其序号分别为s1, s2
//若parent不为0 则说明该结点已经参与了构造 因此不能再次考虑 相当于将该节点从树中排除
//而i不断增加 意味着将新增的 中间节点 也考虑进去了
HT[s1].parent=i; HT[s2].parent=i;//将两节点标记为已访问 并且将他们的parent的位置确定下来
//两个最小的结点的祖先是同一个
HT[i].lchild=s1; HT[i].rchild=s2;//将两个最小值赋给HT的第N+1个结点(两结点的共同祖先的左右孩子确定了)
//在当前数组的末尾新增了中间节点
//循环遍历之后lchild和rchild中存储的是在HT数组中的下标的值
HT[i].weight=HT[s1].weight+HT[s2].weight; //为中间节点的权值赋值便于下一次参与权值的比较
}
}
void getCode1(HuffmanTree HT, HuffmanCode &HC)//从叶子到根求编码
{
char *cd;
int start, c, f, i;
HC=(HuffmanCode)malloc((N+1)*sizeof(char *));
cd=(char *)malloc(N*sizeof(char));
cd[N-1]=’\0′;//标记了tmp中最后一个元素
for(i=1; i<=N; i++)//从第一个元素开始对全部N
{
start=N-1;//将start的位置设置为原有所有叶子节点(未包括中间节点)最后一个元素的下标
//每次循环进行对cd而言都是在叶子开始 因为从叶子开始编码但是从根开始显示
for(c=i, f=HT[i].parent; f!=0; c=f, f=HT[f].parent)//f!=0决定了进行操作的必须是已经筛选出来的元素
{ //该循环从下向上进行 c和f逐个向上移动 即实现了从叶子到根的编码
//当进行到f=0的时候即为根节点 之前0号单元未被占用 因此可以判断走到了根
if(HT[f].lchild==c) //此时是对HT的左右孩子进行判断
//左孩子是0
cd[–start]=’0′;
else// 即为HT的右孩子是c 此时cd的值是1
cd[–start]=’1′;
}
HC[i]=(char*)malloc((N-start)*sizeof(char));//因为HC[i]是HC的一个元素 而HC是二维指针数组
//其中的每个元素都是一个一维指针数组 因此可以每次单独分配空间
//空间的大小就是由每次的start决定的
// 因为start从N-1开始所以数组的大小为N-start
strcpy(HC[i], &cd[start]); //将cd中的元素全部拷贝到当前大小的HC[i]中
}
free(cd);//结束之后 HC中包含多个数组元素每个数组元素中有对应位置原数据的编码
}
void getCode(HuffmanCode &code, char *ch, char *words, HuffmanCode &HC)//子串的编码 原串 子串 原串的哈弗曼编码
{
int i, j, k=0, now=0, m, number=0;
char *c;
code=(HuffmanCode)malloc((strlen(words)+1)*sizeof(char *));
for(i=1; i<=strlen(words); i++)
{
for(j=1; j<=N; j++)
{
if(words[i]==ch[j])
{
code[i]=(char*)malloc((strlen(HC[j]))*sizeof(char));
strcpy(code[i], HC[j]);//printf(“<%s>”, code[i]);
}
}
}
}
void decodeHuffmanTree(HuffmanTree HT,HuffmanCode code, char *word, int len)
{
int p=M;
int i=0, j=1;
while(j<=N)
{
while(i<strlen(code[j]))
{
if(code[j][i]==’0′)
p=HT[p].lchild;
if(code[j][i]==’1′)
p=HT[p].rchild;
if(HT[p].lchild==0 && HT[p].rchild==0)
{
printf(“%c”,word[p]);
p=M;
}
i++;
}
j++;
i=0;
}
}
void Select(HuffmanTree HT, int k, int &s1, int &s2)
{//在HT[1……i-1]中选择parent为0且权值最小的两个结点, 其序号分别为s1, s2
//若parent不为0 则说明该结点已经参与了构造 因此不能再次考虑 相当于将该节点从树中排除
//假设s1对应的权值总是<=s2的权值
unsigned int tmp=MAX, tmpi=0;
int i;
for(i=1; i<=k; i++)
{
if(!HT[i].parent)//parent必须是0 表示没有访问过
{
if(tmp>HT[i].weight)
{
tmp=HT[i].weight;
tmpi=i;
}
}
}//循环结束后tmp是最小的weight权值
s1=tmpi;//此时s1时拥有最小权值的节点的下标
tmp=MAX;//为tmp设定一个最大值 避免在与树中的结点进行比较的时候 初始值小于树中的结点而导致比较失败
tmpi=0;//将所求结点的下标初始化
for(i=1; i<=k; i++)
{
if((!HT[i].parent)&&i!=s1)//parent不为零 保证所找结点未曾使用过
{
if(tmp>HT[i].weight)
{
tmp=HT[i].weight;
tmpi=i;
}
}
}
s2=tmpi;//s2中存储了第二个所求结点的下标
}
void PrintTree(HuffmanTree HT, char ch[])
{
printf(“\ndata, weight, parent, lchild, rchild\n”);
for(int i=1; i<=M; i++)
{
if(i>N)
{
printf(” -, %5d, %5d, %5d, %5d\n”, HT[i].weight, HT[i].parent, HT[i].lchild, HT[i].rchild);
}
else
printf(” %c, %5d, %5d, %5d, %5d\n”, ch[i], HT[i].weight, HT[i].parent, HT[i].lchild, HT[i].rchild);
}
printf(“\n”);
}
void PrintCode(HuffmanCode HC, char ch[])
{
printf(“\n”);
for(int i=1; i<=N; i++)
printf(“%c:%s\n”, ch[i], HC[i]);
printf(“\n”);
}