Huffman树的典型应用是用于二进制编码
可以利用二叉树来设计二进制的前缀编码,约定左分支表示字符“0”,右分支表示字符“1”,为得到电文总长最短的二进制前缀编码就可以利用Huffman树来解决
代码如下:
#include"Huffman.cpp"
#include"StackLink.cpp"
typedef char **HuffmanCode;//动态分配数组空间存储Huffman树编码
void HuffmanCoding(HuffmanTree HT,HuffmanCode &HC,int n);
void Coding(HuffmanTree T,HuffmanCode &HC,int i,LinkStack &s);
void HCTraverse(HuffmanCode HC,int n);
int main()
{
int n;
printf("n=");
scanf("%d",&n);
HuffmanTree HT;
CreateHuffmanTree(HT,n);
HTreeTraverse(HT,n);
HuffmanCode HC;
HuffmanCoding(HT,HC,n);
HCTraverse(HC,n);
return 0;
}
void HuffmanCoding(HuffmanTree HT,HuffmanCode &HC,int n)
{
//先序遍历Huffman树HT,求得树上n个叶子结点的编码存入HC
LinkStack S;
HC=new char* [n];
InitStack_L(S);//初始化栈空间
Coding(HT,HC,HT.root,S);
}
void Coding(HuffmanTree T,HuffmanCode &HC,int i,LinkStack &S)
{
if(i>=0)
{
if((T.HTree[i].lchild==-1)&&(T.HTree[i].rchild==-1))
{
HC[i]=new char[StackLength_L(S)+1];
StackCopytoArray_L(S,HC[i]);//从栈低到栈顶将栈中字符复制到HC[i]中
}
else
{
ElemType e;
Push_L(S,'0');
Coding(T,HC,T.HTree[i].lchild,S);
Pop_L(S,e);
Push_L(S,'1');
Coding(T,HC,T.HTree[i].rchild,S);
Pop_L(S,e);
}
}
}
void HCTraverse(HuffmanCode HC,int n)
{
int i;
cout<<"The code is:"<<endl;
for(i=0;i<n;i++)
cout<<HC[i]<<endl;
}
Huffman.cpp
:
#include<stdio.h>
#include<memory.h>
#include<iostream>
using namespace std;
typedef struct
{
int weight;
int lchild,rchild;
}HTNode;
typedef struct
{
HTNode *HTree;//动态分配数组存储树结点
int root;//根结点的位置
}HuffmanTree;
int arr[100]={0};//标志变量用来标记结点是否已经访问过
int select(HTNode *HTree,int n)
{
int s=0,i;
while(arr[s]==1)
s++;
for(i=s;i<=n;i++)
{
if(HTree[i].weight<HTree[s].weight&&arr[i]==0)
{
s=i;
}
}
arr[s]=1;
return s;
}
void CreateHuffmanTree(HuffmanTree &HT,int n)
{
if(n<=1)
return ;
int m=2*n-1;
//int *arr,i;
//arr=new int[m];//标志变量用来标记结点是否已经访问过
int i;
memset(arr,0,sizeof(arr));
HT.HTree=new HTNode[m];
printf("Elem:");
int temp;
for(i=0;i<n;i++)
{
scanf("%d",&temp);//输入n个权值
HT.HTree[i].weight=temp;
HT.HTree[i].lchild= HT.HTree[i].rchild=-1;//n个带权结点形成初始化森林,每个结点的左右之树为空
}
for(;i<m;i++)
{
HT.HTree[i].weight=0;
HT.HTree[i].lchild= HT.HTree[i].rchild=-1;//初始化尚未使用的结点
}
int s1,s2;
for(i=n;i<m;i++)
{
s1=select(HT.HTree,i-1);//s1,s2是当前最小的两个结点
s2=select(HT.HTree,i-1);
HT.HTree[i].lchild=s1;
HT.HTree[i].rchild=s2;
HT.HTree[i].weight=HT.HTree[s1].weight+HT.HTree[s2].weight;//取左右结点树根结点权值之和
}
HT.root=m-1;
}
void HTreeTraverse(HuffmanTree &HT,int n)
{
printf("**************************************************\n");
cout<<"weight"<<'\t'<<"lchild"<<'\t'<<"rchild"<<endl;
for(int i=0;i<2*n-1;i++)
{
cout<<HT.HTree[i].weight<<'\t'<<HT.HTree[i].lchild<<'\t'<<HT.HTree[i].rchild<<endl;
}
}
int main()
{
int n;
printf("n=");
scanf("%d",&n);
HuffmanTree HT;
CreateHuffmanTree(HT,n);
HTreeTraverse(HT,n);
return 0;
}
StackLink.cpp:
//链栈,链栈中指针的方向是从栈顶指向栈底
#include<stdio.h>
#include<cstring>
#include<iostream>
using namespace std;
typedef char ElemType;
struct LNode
{
ElemType data;
struct LNode *next;
};//S为栈顶指针
typedef LNode *LinkStack;
void InitStack_L(LinkStack &S)
{
S=NULL;
}
void Push_L(LinkStack &S,ElemType e)
{
LinkStack p;
p=new LNode;
p->data=e;
p->next=S;
S=p;
}
bool Pop_L(LinkStack &S,ElemType &e)
{
if(S)
{
LinkStack p=S;
S=S->next;
e=p->data;
delete p;
return true;
}
return false;
}
int StackLength_L(LinkStack S)
{
LinkStack p;
p=S;
int i=0;
while(p)
{
i++;
p=p->next;
}
return i;
}
void StackCopytoArray_L(LinkStack S,char *arr)
{
LinkStack p=S;
int n=StackLength_L(S);
arr[n]='\0';
n--;
while(p)
{
arr[n]=p->data;
n--;
p=p->next;
}
}
过程如下:(由于Huffman中挑选最小的两个结点的顺序不尽相同所以最后得到的编码不完全相同,但是编码的长度是相同的)