哈夫曼编码:
HC[i]是二维的字符串数组,用来存放7个由0和1组成的字符串
cd[start]是字符数组,用来存放不断回溯的过程中得到的0或者1,'/0'是字符串结束标志,放在末尾
最终HC[i]表格为:
基本代码如下:
#include <iostream>
using namespace std;
#include <string>
#include <cstring>
//-----哈夫曼树的存储表示-----
typedef struct{
int weight;//结点的权值
int parent,lchild,rchild;//结点的双亲、左孩子、右孩子的下标
}HTNode,*HuffmanTree;//动态分配数组存储哈夫曼树
void Select(HuffmanTree HT,int n,int &s1,int &s2){
int a= 1e4,b= 1e4;//选两个很大的值进行比大小
for(int i=1;i<=n;i++){
if(HT[i].parent == 0){//一开始双亲都为0
if(HT[i].weight < b){
a = b;
s1 = s2;
//如果找到比b还小的,则将a变为b,s2赋给s1,继承b(原来最小的值)
b = HT[i].weight;
s2 = i;//取出最小的结点编号
}else if(HT[i].weight < a){
/*第一个条件语句满足后,即便第二个条件语句也满足,也不会执行第二个括号。
所以这是用来在第一个条件语句不满足后,用来比较出第二小的*/
a = HT[i].weight;
s1 = i;//取出第二小的结点编号
}
}
}
}
//构造哈夫曼树
void CreateHuffmanTree(HuffmanTree &HT,int n){
//------------初始化哈夫曼树HT------------
if(n<=1) return;
int m=2*n-1;//n个结点构造出哈夫曼树后的结点总个数
HT=new HTNode[m+1]; //0号单元未用,所以需要动态分配m+1个单元,HT[m]表示根节点//HT是指针,指向结点
for(int i=1;i<=m;++i){//将1~m号单元中的双亲、左孩子,右孩子的下标都初始化为0
HT[i].parent=0;
HT[i].lchild=0;
HT[i].rchild=0;
}
cout<<"请输入"<<n<<"个叶子结点的权值:"<<endl;
for(int i=1;i<=n;i++){
cin>>HT[i].weight;//输入前n个单元各结点的权值
}
//------------创建哈夫曼树------------
for(int i=n+1;i<=m;i++){//n+1是新合并出来的第一个结点的编号i
int s1,s2;//因为select函数传的是变量,所以要先声明,如果是定值就不需要先声明
Select(HT,i-1,s1,s2);//在HT[k](1<=k<=i-1)中选择两个其双亲域为0且权值最小的结点,并返回它们在HT中的序号s1和s2
//得到新结点i,从森林中删除s1和s2,将s1和s2的双亲域由0改为i
HT[s1].parent=i;
HT[s2].parent=i;
//s1,s2分别作为i的左右孩子
HT[i].lchild=s1; HT[i].rchild=s2;
//i的权值为左右孩子权值之和
HT[i].weight=HT[s1].weight+HT[s2].weight;
}
}
//输出哈夫曼树
void printHuffmanTree(HuffmanTree &HT,int m){
cout<<"哈夫曼树为:"<<endl;
for(int i=1;i<=m;i++){//注意i不要从0开始,因为0号位没有权值
cout << HT[i].weight << " " << HT[i].parent << " " <<HT[i].lchild << " "<< HT[i].rchild << endl;
}
}
//哈夫曼编码
typedef char **HuffmanCode;//一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针
void CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n){
//从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中
HC=new char*[n+1];//分配存储n个字符编码的编码表空间,指针HC指向他们
/* char *[]是数组,数组中的元素是字符串。在C语言中字符串常量的本质表示其实是一个地址,如char *s="China";等价于char s[]="China";
char *s[]={"China","French","America","German"};*/
char *cd=new char[n]; //分配临时存放每个字符编码的动态数组空间
cd[n-1]='\0';//开辟了n个空间,cd[n-1]是最后一格,放'\0'
for(int i=1;i<=n;i++){
int start=n-1;//start开始时指向最后,即编码结束符位置'\0'
int c=i;//c是结点下标
int f=HT[i].parent;//f指向结点c的双亲结点,f表示当前结点parent域的值
while(f!=0){//f=0意为 直到找到双亲域为0的结点作为根结点
--start;//回溯一次start向前指一个位置
if(HT[f].lchild==c)
cd[start]='0';//结点c是f的左孩子,则生成代码0
else
cd[start]='1';//结点c是f的右孩子,则生成代码1
c=f; f=HT[f].parent;//继续向上回溯
}//求出第i个字符的编码
HC[i]=new char[n-start];//为第i个字符编码分配空间
strcpy(HC[i],&cd[start]);//将求得的编码临时从cd复制到HC的当前行中
}
delete cd;//释放临时空间
}
int main(){
HuffmanTree HT;
int n=8;
CreateHuffmanTree(HT,n);
printHuffmanTree(HT,2*n-1);
HuffmanCode HC;
CreateHuffmanCode(HT,HC,8);
cout<<"哈夫曼编码为:"<<endl;
for(int i=1;i<=8;i++){
cout<< HC[i] <<endl;
}
system("pause");
}