这里我用的实例为:
a-z以及空格的权值为:64 13 22 32 103 21 15 47 57 1 5 32 20 57 63 15 1 48 51 80 23 8 18 1 16 1 168
哈夫曼树的结构定义
typedef struct {
int weight;
int parent, left, right;
}Htnode, * Huffmantree;
typedef char** Huffmancode;
建立哈夫曼树
void select(Huffmantree a, int k, int& s1, int& s2) {//挑选两个最小的叶子
for (int i = 0; i < k; i++) {
if (a[i].parent == -1) {///选出一个第一个没有父子节点的
s1 = i;
break;
}
}
for (int i = 0; i < k; i++) {
if (a[i].parent == -1 && a[s1].weight > a[i].weight) {//找到没有父节点中,值最小的节点
s1 = i;
}
}
a[s1].parent = 1;//当被选后,这个节点的的父节点应该标记为有
for (int i = 0; i < k; i++) {
if (a[i].parent == -1 && i != s1) {//同理s1,但注意不能和s1重合
s2 = i;
break;
}
}
for (int i = 0; i < k; i++) {
if (a[i].parent == -1 && a[s2].weight > a[i].weight) {
s2 = i;
}
}
a[s2].parent = 1;
}
void creatHuffmantree(Huffmantree& ht, int n) {//建立权值
int s1 = 0, s2 = 0;
if (n <= 1)
return;
int m = 2 * n - 1;
ht = new Htnode[m + 1];
for (int i = 0; i < m; i++) {//初始化各个节点,没有标记为-1,
ht[i].parent = -1;
ht[i].left = -1;
ht[i].right = -1;
ht[i].weight = 0;
}
for (int i = 0; i < n; i++) {
//cout<<char(i+'a')<<":";
cin >> ht[i].weight;//给出各个节点的权值
//cout<<endl;
}
for (int i = n; i < m; i++) {
select(ht, i, s1, s2);//选出前i个节点中最小的两个叶子节点
ht[s1].parent = i;//两个叶子节点的父节点为i
ht[s2].parent = i;
ht[i].left = s1;//s1,s2 分别为只有子节点
ht[i].right = s2;
ht[i].weight = ht[s1].weight + ht[s2].weight;//父节点的权值变为左右节点权值之和
//cout << char(i + 'a') << " " << ht[i].left << " " << ht[i].right << " " << ht[i].weight << endl;
}
}
哈夫曼树编码
void creatHuffmancode(Huffmantree HT, Huffmancode& HC, int n)
{
//用来保存指向每个赫夫曼编码串的指针
HC = (Huffmancode)malloc(n * sizeof(char*));
//临时空间,用来保存每次求得的赫夫曼编码串
//对于有n个叶子节点的赫夫曼树,各叶子节点的编码长度最长不超过n-1
//外加一个'\0'结束符,因此分配的数组长度最长为n即可
char* code = (char*)malloc(n * sizeof(char));
if (!code)
{
printf("code malloc faild!");
exit(-1);
}
code[n - 1] = '\0'; //编码结束符,亦是字符数组的结束标志
//求每个字符的赫夫曼编码
int i;
for (i = 0; i < n; i++)
{
int current = i; //定义当前访问的节点
int father = HT[i].parent; //当前节点的父节点
int start = n - 1; //每次编码的位置,初始为编码结束符的位置
//从叶子节点遍历赫夫曼树直到根节点
while (father != -1)
{
if (HT[father].left == current) //如果是左孩子,则编码为0
code[--start] = '0';
else //如果是右孩子,则编码为1
code[--start] = '1';
current = father;
father = HT[father].parent;
}
//为第i个字符的编码串分配存储空间
HC[i] = (char*)malloc((n - start) * sizeof(char));
//将编码串从code复制到HC
strcpy(HC[i], code + start);
}
for (int i = 0; i < n; ++i) {
if (i == 26) {
cout << " :";
}
else
cout << char('a' + i) << ":";
printf("%s\n", HC[i]);
}
free(code); //释放保存编码串的临时空间
}
哈夫曼树解码
void encode(string s) {//字符变编码
int len = s.size();
//64 13 22 32 103 21 15 47 57 1 5 32 20 57 63 15 1 48 51 80 23 8 18 1 16 1 168
for (int i = 0; i < len; i++) {
if (s[i] == ' ')
{
cout << hc[26];
//cout<<"i:"<<i<<" q"<<len<<endl;
//continue;
}
else {//输出对应字符的编码
int x = s[i] - 'a';
cout << hc[x];
} //cout<<i<<endl;
}
}
void decode(string s) {//编码变为字符
// int len=s.size();
string ans = "";
string ts = "";
string temp[27];
//把char变为string类型,后面字符匹配方便
for (int i = 0; i < 27; i++) {
int len = strlen(hc[i]);
for (int j = 0; j < len; j++) {
temp[i] += hc[i][j];
}
}
for (int i = 0; i < s.size(); i++)
{
ts += s[i];//一直读取后面的编码直到有字符与其对应
for (int j = 0; j < 27; j++)//每次都和编码进行匹配,因为哈夫曼编码一定没有前缀重合
{
if (ts == temp[j])//匹配成功,将目标字符串清空
{
ts = "";//将前面的清空,继续寻找下一组匹配的编码
if (j != 26)
{
ans += j + 'a';
}
else if (j == 26)
{
ans += ' ';
}
}
}
//cout<<ans<<endl;
}
cout << ans << endl;
}