#include<iostream>
#include<conio.h>
#include <iomanip>
#include<stdlib.h>
using namespace std;
typedef struct{
unsigned int weight;
unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef char*HuffmanCode;
typedef struct{
int num;
int value;
}myHuff;
int HuffDepth(HuffmanTree HT,int i)//返回树深度
{
int dep=0;
while(1)
{
if(HT[i].lchild ==0)
{
break;
}
else
{
dep++;
i=HT[i].lchild;
}
}
return dep;
}
//动态选取哈夫曼二叉树节点最小两个值
void Select(const HuffmanTree HT, int n, int &s1,int &s2)
{
myHuff *a=new myHuff[n+1]; //定义结构体数组拷贝哈夫曼数组中的数据进行排序取值,主要目的是保护原来的哈夫曼数据
myHuff temp;//交换数据时的空容器
int k,i,j;
for(i=1;i<n+1;i++)//拷贝哈夫曼数据
{
a[i].value=HT[i].weight;
a[i].num=i;
}
for(i=1;i<n;i++)//对数据进行选择排序
{
k=i;
for(j=i+1;j<n+1;j++)
{
if(a[j].value >a[k].value )
{
k=j;
}
}
if(k!=i)
{
temp=a[k];
a[k]=a[i];
a[i]=temp;
}
}
for(i=1;i<n+1;i++)//此处是为了保持数据排序的一致性,保持新数据在后,老数据在前
{
if(a[i].value==a[i+1].value&&HuffDepth(HT,a[i].num)==0)
{
temp=a[i+1];
a[i+1]=a[i];
a[i]=temp;
}
}
int p=n;//下面为取最小两数据的具体算法,有点复杂
while(1)
{
if(HT[a[p].num].parent ==0)
{
s1=a[p].num;
p--;
break;
}
p--;
}
while(1)
{
//选取没有父节点,且a的下标要大于1的p(否则会数据溢出)
if(p!=1&&HT[a[p].num].weight ==HT[a[p-1].num].weight&&HT[a[p].num].parent ==0&&HT[a[p-1].num].parent ==0)
{
if(HT[a[p].num].parent ==0&&HuffDepth(HT,a[p].num)==0)//在数值相同的情况下比较节点深度
{
s2=a[p].num;
p--;
break;
}
else
{
p--;
}
}
if(HT[a[p].num].parent ==0)
{
s2=a[p].num;
p--;
break;
}
p--;
}
delete []a;
}
void HuffmanCoding(HuffmanTree &HT, HuffmanCode *HC, int *w, int n) {
// 算法6.12
// w存放n个字符的权值(均>0),构造哈夫曼树HT,
// 并求出n个字符的哈夫曼编码HC
int i, j, m, s1=0, s2=0, start;
char *cd;
unsigned int c, f;
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;
}
printf("\n哈夫曼树的构造过程如下所示:\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(" 按任意键,继续 ...");
getch();
for (i=n+1; i<=m; i++) { // 建哈夫曼树
// 在HT[1..i-1]中选择parent为0且weight最小的两个结点,
// 其序号分别为s1和s2。
Select(HT, i-1, s1, s2);
HT[s1].parent = i; HT[s2].parent = i;
HT[i].lchild = s1; HT[i].rchild = s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;
printf("\nselect: s1=%d s2=%d\n", s1, s2);
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(" 按任意键,继续 ...");
getch();
}
//--- 从叶子到根逆向求每个字符的哈夫曼编码 ---
cd = (char *)malloc(n*sizeof(char)); // 分配求编码的工作空间
cd[n-1] = '\0'; // 编码结束符。
for (i=1; i<=n; ++i) { // 逐个字符求哈夫曼编码
start = n-1; // 编码结束符位置
for (c=i, f=HT[i].parent; f!=0; c=f, f=HT[f].parent)
// 从叶子到根逆向求编码
if (HT[f].lchild==c) cd[--start] = '0';
else cd[--start] = '1';
HC[i] = (char *)malloc((n-start)*sizeof(char));
// 为第i个字符编码分配空间
strcpy(HC[i], &cd[start]); // 从cd复制编码(串)到HC
}
free(cd); // 释放工作空间
} // HuffmanCoding
int main()
{
int a[8]={5,29,7,8,14,23,3,11};
HuffmanTree HT;
HuffmanCode *HC=new HuffmanCode[9];
HuffmanCoding(HT,HC,a,8);
cout<<endl<<"得到的哈夫曼编码如下:"<<endl;
for(int i=1;i<=8;i++)
{
cout<<setw(8)<<HT[i].weight<<setw(8)<<HC[i]<<endl;
}
delete []HC;
printf(" 按任意键,继续 ...");
getch();
return 0;
}