一.哈夫曼树
1.什么是哈夫曼树?
哈夫曼树又称最又树,是一类带权路径长度最短的树。
2.哈夫曼树的特点
哈夫曼树中权值越大的离根越近,权值越小的离根越远。
在哈夫曼树中只有度为2或者度为0的结点,没有度为1的结点,这与哈夫曼树的构造过程有关。
3.如何构造哈夫曼树
构造过程:(1):构造树林全是根
(2):选择两小造新树
(3):删除两小添新人
(4):重复(2)(3)只剩根
算法:
初始化:首先动态申请2n个单元;循环2n-1次,从1号单元开始,依次从1至2n-1所有单元的双亲,左右孩子,都初始化为0;最后循环n次,输入前n个单元中叶子结点的权值
创建树:循环n-1次,通过n-1次的选择,删除合并来创建哈夫曼树。选择是从树林中选择双亲为0且权值最小的两个树根结点s1和s2;删除是指将结点s1和s2的双亲改为非0;合并就是将s1和s2的权值和作为一个新结点的权值依次存入数组的n+1号及之后的单元中,同时记录这个新结点的左孩子下标为s1,右孩子下标为s2。
void CreatHuff(HuffmanTree **T,int n)
{
if(n<=1) return;
int m = 2*n-1;//哈夫曼树的总结点数
*T = (HuffmanTree*)malloc(sizeof(HuffmanTree)*(m+1));//不用0号元素
for(int i = 1;i<=m;i++)
{
(*T)[i].lchild = (*T)[i].rchild = (*T)[i].parent = 0;//初始化
}//这里用数组访问元素应该用.因为传入的参数实际是数组名 并不是每一个使用的元素都是指针 只是为了传入数组
for(int i = 1;i<=n;i++)
{
int tmp = 0;
scanf("%d",&tmp);
//getchar();
(*T)[i].weight = tmp;//给前n个结点权值
}
for(int i = n+1;i<=m;i++)//i初始化为新结点的开始
{
int p1,p2;
p1 = p2 = 0;
Select(*T,i,&p1,&p2);//p1 p2传回来的是最小权值
int s1,s2;
s1 = s2 = 0;
int flag = 0;
for(int j = 1;j<=i-1;j++)
{
if((*T)[j].weight==p1&&(*T)[j].parent==0)
{
if(flag==0)//防止s1与s2输入的值一样
s1 = j;
flag =1;
}
if((*T)[j].weight==p2&&(*T)[j].parent==0)
s2 = j;
if(s1!=0&&s2!=0&&s2!=s1)
break;
}//搜索权值最小的结点的序号
(*T)[i].weight = p1+p2;
(*T)[i].lchild = s1;
(*T)[i].rchild = s2;
(*T)[s1].parent = i;
(*T)[s2].parent = i;
}
for(int i = 1;i<=m;i++)
{
printf("%d %d %d %d\n",(*T)[i].weight,(*T)[i].parent,(*T)[i].lchild,(*T)[i].rchild);//遍历一下看构建是否正确
}
}
选择最小权值函数实现:
这里使用的是冒泡排序,时间复杂度为O(n*n)
void Select(HuffmanTree *T,int n,int *p1,int *p2)//p1,p2为挑选出来权值最小的两个结点
{//用冒泡排序来选择
//首先把没有双亲的结点存储在另一个数组中
int array[n];
int s = 0;
for(int i = 1;i<=n-1;i++)
{
if(T[i].parent==0)
{
array[s] = T[i].weight;
s++;
}
}
if(s==0) return;
for(int i = 0;i<s-1;i++)
{
for(int j = 0;j<s-1;j++)
{
if(array[j]>array[j+1])
{
int tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
}
}
}
*p1 = array[0];
*p2 = array[1];
}
二.哈夫曼编码
在构建哈夫曼树后,求哈夫曼编码的主要思想是:依次以叶子结点为出发点,向上回溯至根结点为止。回溯时走左分支生成代码0,右分支生成代码1。
为什么哈夫曼编码能够保证字符编码总长最短?
因为哈夫曼树的带权路径最短,出现频率高的字符,编码最短。
代码实现如下:
void _CreatHuff(HuffmanTree *T,int n,HuffmanCode **TC)
{
int m = 2*n-1;//总结点树
*TC = (HuffmanCode*)malloc(sizeof(HuffmanCode)*(n+1));
for(int i = 1;i<=n;i++)
{
(*TC)[i].weight = i;
(*TC)[i].str = (char*)malloc(sizeof(char)*10);
}
for(int i = 1;i<=n;i++)
{
int j = i;
int k = 0;
int x = i;
printf("第%d个结点的哈夫曼编码:",i);
do{
int tmp = j;
for(int i = n+1;i<=m;i++)
{
if(T[i].lchild==tmp)
{
(*TC)[x].str[k]= 0;
k++;
break;
}
if(T[i].rchild==tmp)
{
(*TC)[x].str[k] = 1;
k++;
break;
}
}
j = T[j].parent;
}while(T[j].parent!=0);
for(int i = k-1;i>=0;i--)//逆序输出
{
printf("%d",(*TC)[x].str[i]);
}
putchar('\n');
}
}

2万+

被折叠的 条评论
为什么被折叠?



