根据给定的n个权值生成哈夫曼二叉树,输出赫夫曼编码及进行译码
哈夫曼树的结点的结构体
struct huffmannode//哈夫曼树结点的结构体
{
int weight;//结点权重
int parent;//父结点
int leftchild;//左子树
int rightchild; //右子树
};
建树
void creat_tree(huffmannode tree[], int w[],int n)//建哈夫曼树
{
int node1 = 0, node2 = 0;
for(int i=0;i<2*n-1;++i)//初始化结点
{
tree[i].parent = -1;
tree[i].leftchild = -1;
tree[i].rightchild = -1;
}
for(int i=0;i<n;++i)//将输入的结点值输入哈夫曼树
{
tree[i].weight = w[i];
}
for(int k=n;k<2*n-1;++k)
{
select(tree,k,node1,node2);//找到最小结点,次小结点
tree[k].weight = tree[node1].weight + tree[node2].weight;//生成新结点,权重为两结点之和
tree[node1].parent = k;//最小结点父结点为新结点
tree[node2].parent = k;//次小结点父结点为新结点
tree[k].leftchild = node1;//新结点左子树为最小结点
tree[k].rightchild = node2;//新结点右子树为次小结点
}
}
思路:将最开始输入的结点放入待选择结点列表,不断选择权重最小与权重次小的结点,用他们生成新的结点,并使新结点成为他们的父结点,再将新结点也放入待选择的结点列表,直至新生成了n-1个结点,此时该父结点也为根结点,最后这2*n-1个结点就成为了哈夫曼树。
建树时使用的查找函数
void select(huffmannode tree[],int k,int &node1,int &node2)//在尚未使用的结点中,找到两个权重最小的结点
{
node1 = -1, node2 = -1;//初始化两个没有父结点的最小结点的位置
int minweight = 1e8;//初始化最小权重
for(int i=0;i<k;++i)
{
if(tree[i].parent==-1)//没有父结点
{
if(tree[i].weight<minweight)//小于最小权重
{
minweight = tree[i].weight;//更新最小权重
node1 = i;//更新最小点位置
}
}
}
minweight = 1e8;//初始化权重
for(int i=0;i<k;++i)
{
if(tree[i].parent==-1&&i!=node1)//没有父结点且不是最小结点
{
if(tree[i].weight<minweight)//更新次小结点
{
minweight = tree[i].weight;
node2 = i;
}
}
}
}
思路:两遍遍历,第一遍找到未连接父结点且权重最小的点,将其标记为node1;第二遍,找到除了node1之外,未连接父结点且权重最小的点,将其标记为node2.
生成结点对应的哈夫曼编码
void form_code(huffmannode tree[],int n)//生成哈夫曼编码
{
int cur, parent, start;
string huffcode;
for(int i=0;i<n;++i)//遍历n个输入的结点
{
cur = i;
parent = tree[i].parent;
while(parent!=-1)//当没有到达根结点,向上寻找父结点
{
if(tree[parent].leftchild==cur)//如果当前结点是左子树
{
huffcode = '0' + huffcode;//在哈夫曼编码前加0
}
else
{
huffcode = '1' + huffcode;//加1
}
cur = parent;//更新当前结点
parent = tree[parent].parent;//更新父结点
}
code[tree[i].weight] = huffcode;//将生成的哈夫曼编码用map保存
translate[huffcode] = ch[i];//将哈夫曼编码对应的值保存
huffcode.clear();//清空哈夫曼编码
}
}
思路:分别以n个输入的结点为起点,不断向上寻找它的父结点,直到父结点到达根结点,如果对于父结点来讲,当前结点是左子结点,则在已生成的哈夫曼编码前加上0,如果是右子结点,则加上1。在生成完整编码后用map容器保存编码。
完整代码
#include<iostream>
#include<cstdio>
#include<string>
#include<map>
using namespace std;
const int Size = 1e4+5;
struct huffmannode//哈夫曼树结点的结构体
{
int weight;//结点权重
int parent;//父结点
int leftchild;//左子树
int rightchild; //右子树
};
map<int,string> code;//保存值对应的哈夫曼编码
map<string,char> translate;//保存哈夫曼编码对应的值
map<char,int> alp;
char ch[105];
int w[105];
void select(huffmannode tree[],int k,int &node1,int &node2)//在尚未使用的结点中,找到两个权重最小的结点
{
node1 = -1, node2 = -1;//初始化两个没有父结点的最小结点的位置
int minweight = 1e8;//初始化最小权重
for(int i=0;i<k;++i)
{
if(tree[i].parent==-1)//没有父结点
{
if(tree[i].weight<minweight)//小于最小权重
{
minweight = tree[i].weight;//更新最小权重
node1 = i;//更新最小点位置
}
}
}
minweight = 1e8;//初始化权重
for(int i=0;i<k;++i)
{
if(tree[i].parent==-1&&i!=node1)//没有父结点且不是最小结点
{
if(tree[i].weight<minweight)//更新次小结点
{
minweight = tree[i].weight;
node2 = i;
}
}
}
}
void creat_tree(huffmannode tree[], int w[],int n)//建哈夫曼树
{
int node1 = 0, node2 = 0;
for(int i=0;i<2*n-1;++i)//初始化结点
{
tree[i].parent = -1;
tree[i].leftchild = -1;
tree[i].rightchild = -1;
}
for(int i=0;i<n;++i)//将输入的结点值输入哈夫曼树
{
tree[i].weight = w[i];
}
for(int k=n;k<2*n-1;++k)
{
select(tree,k,node1,node2);//找到最小结点,次小结点
tree[k].weight = tree[node1].weight + tree[node2].weight;//生成新结点,权重为两结点之和
tree[node1].parent = k;//最小结点父结点为新结点
tree[node2].parent = k;//次小结点父结点为新结点
tree[k].leftchild = node1;//新结点左子树为最小结点
tree[k].rightchild = node2;//新结点右子树为次小结点
}
}
void form_code(huffmannode tree[],int n)//生成哈夫曼编码
{
int cur, parent, start;
string huffcode;
for(int i=0;i<n;++i)//遍历n个输入的结点
{
cur = i;
parent = tree[i].parent;
while(parent!=-1)//当没有到达根结点,向上寻找父结点
{
if(tree[parent].leftchild==cur)//如果当前结点是左子树
{
huffcode = '0' + huffcode;//在哈夫曼编码前加0
}
else
{
huffcode = '1' + huffcode;//加1
}
cur = parent;//更新当前结点
parent = tree[parent].parent;//更新父结点
}
code[tree[i].weight] = huffcode;//将生成的哈夫曼编码用map保存
translate[huffcode] = ch[i];//将哈夫曼编码对应的值保存
huffcode.clear();//清空哈夫曼编码
}
}
void print_code(huffmannode tree[],int n)//输出
{
cout << "得到的哈夫曼值为" << endl;
for(int i=0;i<n;++i)
{
cout << ch[i] << ":" << tree[i].weight << "的哈夫曼值为" << code[tree[i].weight] << endl;
}
}
void translate_code(string unknown)//翻译编码
{
string s;
for(int i=0;i<unknown.size();++i)//读入未知编码
{
s = s + unknown[i];
if(translate[s]!=0)//已读入部分成功匹配现有编码
{
cout << translate[s];//输出对应值
s.clear();//清空已读入部分
}
}
cout << endl;
}
void translate_string(string unknown)//编码字符串
{
for(int i=0;i<unknown.size();++i)//读入未知编码
{
cout << code[alp[unknown[i]]];
}
cout << endl;
}
int main()
{
int n;
cout << "请输入结点的数量(解码过程)" << endl;
cin >> n;
cout << "请输入结点各自的字符和值" << endl;
for(int i=0;i<n;++i)
{
cin >> ch[i];
cin >> w[i];
alp[ch[i]] = w[i];
}
huffmannode *tree = new huffmannode[2*n-1];
creat_tree(tree,w,n);
form_code(tree,n);
print_code(tree,n);
string unknown;
cout << "请输入一段待翻译的01字符串(译码过程)" << endl;
cin >> unknown;
cout << "翻译结果为" << endl;
translate_code(unknown);
cout << "请输入一段字符串(编码过程)" <<endl;
cin >> unknown;
cout << "转码结果为" << endl;
translate_string(unknown);
return 0;
}