#include<string.h>
#include<stdlib.h>
#include<stdio.h>
int m,xiao,dia;
typedef struct {
unsigned int weight;
unsigned int parent,lchild,rchild;
}HTNode;
typedef HTNode * HuffmanTree; //动态分配数组存储哈夫曼树
typedef char * HuffmanCode; //动态分配数组存储哈夫曼编码表
void Select(HuffmanTree HT,int n)
{
int i,j;
for(i = 1;i <= n;i++)
if(!HT[i].parent){xiao = i;break;}
for(j = i+1;j <= n;j++)
if(!HT[j].parent){dia = j;break;}
for(i = 1;i <= n;i++)
if((HT[xiao].weight>HT[i].weight)&&(!HT[i].parent)&&(dia!=i))
xiao=i; //找到最小值的索引
for(j = 1;j <= n;j++)
if((HT[dia].weight>HT[j].weight)&&(!HT[j].parent)&&(xiao!=j))
dia=j; //找到比xiao大的索引
}
void HuffmanCoding(HuffmanTree HT, HuffmanCode HC[], int *w, int n) {
// 算法6.13
// w存放n个字符的权值(均>0),构造哈夫曼树HT,
// 并求出n个字符的哈夫曼编码HC
int i, j;
char *cd;
int p;
int cdlen;
if (n<=1) return;
m = 2 * n - 1; //所有的顶点数量,树叶数量加上内点数量
HT = (HuffmanTree)malloc((m+1) * sizeof(HTNode)); // 0号单元未用
for (i=1; i<=n; i++) { //初始化
HT[i].weight=w[i-1];
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
for (i=n+1; i<=m; i++) { //初始化
HT[i].weight=0;
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
puts("\n哈夫曼树的构造过程如下所示:");
printf("HT初态:\n 结点 weight parent lchild rchild");
for (i=1; i<=m; i++)
printf("\n%4d%8d%8d%8d%8d",i,HT[i].weight, HT[i].parent,HT[i].lchild, HT[i].rchild);
printf(" 按任意键,继续 ...");
getchar();
for (i=n+1; i<=m; i++) // 建哈夫曼树
{
// 在HT[1..i-1]中选择parent为0且weight最小的两个结点,
// 其序号分别为s1和s2。
Select(HT, i-1);
HT[xiao].parent = i; HT[dia].parent = i;
HT[i].lchild = dia; HT[i].rchild = xiao;
HT[i].weight = HT[xiao].weight + HT[dia].weight;
printf("\nselect: dia=%d xiao=%d\n", dia, xiao);
printf(" 结点 weight parent lchild rchild");
for (j=1; j<=i; j++)
printf("\n%4d%8d%8d%8d%8d",j,HT[j].weight, HT[j].parent,HT[j].lchild, HT[j].rchild);
printf(" 按任意键,继续 ...");
getchar();
while(getchar()!='\n')
continue; //删除多余的换行符
}
//------无栈非递归遍历哈夫曼树,求哈夫曼编码
cd = (char *)malloc(n*sizeof(char)); // 分配求编码的工作空间
p = m; cdlen = 0;
for (i=1; i<=m; ++i) // 遍历哈夫曼树时用作结点状态标志
HT[i].weight = 0;
while (p)
{
if (HT[p].weight==0)
{ // 向左
HT[p].weight = 1;
if (HT[p].lchild != 0)
{
p = HT[p].lchild;
cd[cdlen++] ='0';
}
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';
}
}
else
{ // HT[p].weight==2,退回退到父结点,编码长度减1
HT[p].weight = 0; p = HT[p].parent; --cdlen;
}
}
} // HuffmanCoding
int main(void) {
HuffmanTree HT;HuffmanCode *HC;int *w,n,i;
puts("输入结点数:");
scanf("%d",&n);
HC = (HuffmanCode *)malloc(n*sizeof(HuffmanCode));
w = (int *)malloc(n*sizeof(int));
printf("输入%d个结点的权值\n",n);
for(i = 0;i < n;i++)
scanf("%d",&w[i]);
HuffmanCoding(HT,HC,w,n);
puts("\n各结点的哈夫曼编码:");
for(i = 1;i <= n;i++)
printf("%2d(%4d):%s\n",i,w[i-1],HC[i]);
getchar();
}