14天阅读挑战赛
努力是为了不平庸~
代码
#include<iostream>
#include<string.h>
using namespace std;
//顺序存储的数组元素结点数据结构
typedef struct
{
int weight;//节点权值
int parent,lchild,rchild;//节点的双亲,左孩子,右孩子的下标
} HTNode,*HuffmanTree;
//定义Huffman编码表,每一行对应一个原文字符的Huffman编码
//以字符串形式存储编码结果
typedef char **HuffmanCode;
//Huffman树构造过程中,贪心选择函数
void Select(HuffmanTree HT,int len,int &s1,int &s2)
{
//HT为存储的二叉树的结构体数组,len为选择的范围,主调函数中的从1到i-
//1位置
//构建过程中直接对HT数组中的元素信息进行更新维护
int i=1,min1=0x3f3f3f3f,min2=0x3f3f3f3f;//先赋予最大值
while(i<=len)
{
if(HT[i].weight<=min1 && HT[i].parent==0)
//如果权重小于最小的min1,将min1的值赋给min2,min1赋值 最小的
//并将相应的序号,赋给s1 s2
{
min2=min1;
s2=s1;
min1=HT[i].weight;
s1=i;
}
else if(HT[i].weight<=min2 && HT[i].parent==0 && HT[i].weight>min1)
//如果仅剩两个数,且序号大的数大,将大的数赋给s2
{
min2=HT[i].weight;
s2=i;
}
i++;
}
}
//求权重,w数组,记录权重,c数组,记录对应的字符
void getweight(int w[],char c[])
{
string str;
getline(cin,str);//输入带空格的字符串
int len=str.size(); //求字符串长度
int a[60][60]; // 定义一个二维数组
int i=1,j=1,k=0,p=1; //a[i][0]记录字符,a[i][1]记录权重
a[i][0]=str[k]; //先记录第一个字符
c[i]=str[k];
a[i][1]++; //该字符对应的权重++
k++;
while(k<len)
{
i=1; //每次都从第一个记录的字符开始比较
while(a[i][0]!=0)
{
if(a[i][0]==(int)str[k]) //如果 输入的字符与记录字符的相等
// 权重++,并跳出while循环
{
a[i][1]++;
k++;
p=0;
break;
}
else //不相等换下一个,进行比较
{
i++;
}
}
if(p) //p是作为判断,是否有与之对应的字符
{ //若没有,重新赋值一个
a[i][0]=str[k];
c[i]=str[k];
}
else //如果有,重新对p 赋值为1
{
p=1;
}
}
i=1;w[0]=0;
while(a[i][0]!=0) //对记录权重的数组 w进行赋值
{
w[i]=a[i][1];
i++;
}
}
//构造Huffman树功能函数,n表示有n个叶子结点
void CreatHuffmanTree(HuffmanTree &HT,int n)
{
//构造Huffman树HT,m存放根据叶子结点数计算的Huffman树结点总数
//s1和s2分别表示HT结构体数组下标,用来指示元素所在位置
int m,s1,s2,i=1;
int w[60]={0};
char c[60];
getweight(w,c);//求权重
if (n<=1) return;
m=2*n-1;
HT=new HTNode[m+1]; //0号单元未用,所以需要动态分配 m+1个单元,HT[m]
//表示根结点
for(i=1;i<=m;i++)//将1-m号单元中的双亲,左孩子,右孩子的下标都初始化为0
{
HT[i].parent=0;HT[i].lchild=0;HT[i].rchild=0;
}
for(i=1;i<=n;i++) //输入前n个单元中叶子节点的权值
{
HT[i].weight=w[i];
}
for(int i=n+1;i<=m;i++)//通过n-1次的选择、删除、合并来创建哈夫曼树
{
Select(HT,i-1,s1,s2);
// 选择两个双亲域为0且权值最小的节点,并返回它们在HT中的序号s1,s2
HT[s1].parent=i;HT[s2].parent=i;
//得到新节点i,从森林中删除s1,s2,将s1和s2的双亲域由0改为1
HT[i].lchild=s1;HT[i].rchild=s2; // s1,s2分别作为i的左右孩子
HT[i].weight=HT[s1].weight+HT[s2].weight; // i的权重为左右孩子权值之和
}
/*――――――――――从n+1开始到m结束,构建Huffman树HT中的非叶子结点――――――――――*/
}
void CreatHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n)
{
//从叶子到根逆向求每个字符的Huffman编码,存储在编码表HC中,HT为构造的
//Huffman树,n为要编码的字符数,即叶子节点数
int i,start,c,f;
HC=new char*[n+1]; //分配n个字符编码的头指针矢量
char *cd=new char[n]; //分配临时存放编码的动态数组空间
cd[n-1]='\0'; //编码结束符
for(int i=1;i<=n;i++) //逐个字符求哈夫曼编码
{
start=n-1; //start开始时指向最后,即编码结束符位置
c=i;f=HT[i].parent; //f指向节点c的双亲节点
while(f!=0) //从叶子节点开始向上回溯,直到根节点
{
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; //继续向上回溯
}
HC[i]=new char[n-start]; //为第i个字符编码分配空间
strcpy(HC[i],&cd[start]); //将求得的编码从临时空间cd复制到HC的当前行中
}
/*――――――――――根据HT逐个构建HC中的Huffman编码――――――――
――*/
delete cd; //释放临时空间
}
//Huffman编码输出函数,HT[i]字符的编码为 HC[i]字符串中的内容
void show(HuffmanTree HT,HuffmanCode HC,int n)
{
for (int i=1; i<=n; i++)
cout<<HT[i].weight<<" 编码为 "<<HC[i]<<endl;
}
int main()
{
HuffmanTree HT; //定义一个哈夫曼树
HuffmanCode HC; //定义一个哈夫曼编码
int n,m;
cout<<"请输入叶子结点的个数:\n";
cin>>n; //输入Huffman树的叶子结点个数
m=2*n-1;
getchar(); //吸收“\n”
cout<<"请输入数据: \n";
CreatHuffmanTree(HT,n); //创建哈夫曼树
CreatHuffmanCode(HT,HC,n);//求对应的哈夫曼编码
show(HT,HC,n); //输出每个权重对应的哈夫曼编码
cout<<"输入文本:\n";
string str;
getline(cin,str);//重新输入数据
int len=str.size();//求输入字符串的长度
cout<<"再次输入数据:\n" ;
int w[60];
char c[60];
getweight(w,c); //求字符数组c
for(int i=1;i<=n;i++)
{
cout<<c[i]<<" "<<HC[i]<<endl;//输出每个字符对应的哈夫曼编码
}
cout<<"导出编码后的文本 : \n";
for(int i=0;i<len;i++)//遍历整个字符串
{
int j=1,p=1; //p作为判断语句
while(p)
{
if(c[j]==str[i])//若字符与字符数组c中的相同,输出对应的哈夫曼编码
{
cout<<HC[j];
p=0; //把p置为0,使循环结束
}
else //不相等,比较下一个
{
j++;
}
}
}
cout<<endl;
cout<<"输入编码串;\n";
string er;
cin>>er; //重新输入得到的编码串,用er储存
cout<<"输出译码后文本:\n";
int lon=er.size(); //求编码串的长度
int k=m; //k赋值为根节点序号m
for(int i=0;i<lon;i++)
{
if(er[i]=='0') //如果输入的编码为0,则走向左孩子
{
k=HT[k].lchild;
}
else // 如果输入的编码为1,则走向右孩子
{
k=HT[k].rchild;
}
if(HT[k].lchild==HT[k].rchild)//如果一个节点的左右孩子序号相等,也就是都为空
{ //说明该节点为叶子节点,输出对应的字符
cout<<c[k];
k=m; //重新从根节点进行遍历
}
}
}