一、定义哈夫曼树结构
- 相关概念
- 路径长度: 结点所在层数为 i i i, 路 径 长 度 = 层 数 i − 1 路径长度=层数i-1 路径长度=层数i−1
- 结点的权: 就是该节点的 w e i g h t weight weight
- 结点的带权路径长度=路径长度*结点的权
- 树的带权路径长度: 树中所有叶子结点的带权路径长度之和。通常记作 “WPL”。
- 哈夫曼树: 也叫最优树、最优二叉树。当用 n 个结点(都做叶子结点且都有各自的权值)试图构建一棵树时,如果构建的这棵树的带权路径长度最小,称这棵树为“最优二叉树”,有时也叫“赫夫曼树”或者“哈夫曼树”。
- 构建原则: 在构建哈弗曼树时,要使树的带权路径长度最小,只需要遵循一个原则,那就是:权重越大的结点离树根越近。
- 构建哈夫曼树的过程:
对于给定的有各自权值的 n 个结点,构建哈夫曼树有一个行之有效的办法:- 在 n 个权值中选出两个最小的权值,对应的两个结点组成一个新的二叉树,且新二叉树的根结点的权值为左右孩子权值的和;
- 在原有的 n 个权值中删除那两个最小的权值,同时将新的权值加入到 n–2 个权值的行列中,以此类推;
- 重复 1 和 2 ,直到所以的结点构建成了一棵二叉树为止,这棵树就是哈夫曼树。
![](https://img-blog.csdnimg.cn/51adf6be14374fdeb95ed6150bf6eeb8.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAQmV0dGVyLWluZw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
struct huff
{
char c;//字符
int data;//权值
int lch, rch,p;//左右孩子节点及父节点在数组中的下标
int tag;//是否已并入树中
};
二、数据定义
huff h[26];存储叶子节点
int n;n为叶子节点个数
int t;节点个数
char enterstr[100];存储输入字符串
char entercode[100];存储输入的哈夫曼编码
char realstr[100];输出解码结果
code cc[100];用于存储每个字符的哈夫曼编码
三、哈夫曼树构建函数
//功能: 从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树
int hufftree()
{
cout << "输入叶子节点个数" << endl;
cin >> n;//n为叶子节点个数
for (int j = 0; j < n; j++)
{
cout << "输入字符、权值";
cin >> h[j].c >> h[j].data;
h[j].lch = -1; h[j].rch = -1; h[j].tag = 0;h[j].p=-1;//初始化数据
}
int i = 0;
while (i < n - 1)//合并n-1次,因为总共有2n-1个节点,n为叶子节点数
{
int x1 = 0, m1 = 32767;//m1是最小值单元,x1为下标号
int x2 = 0, m2 = 32767;//m2是次小值单元,x2为下标号
for (int j = 0; j < n + i; j++)
{
if ((h[j].data < m1) && (h[j].tag == 0))
{
m2 = m1; x2 = x1;
m1 = h[j].data; x1 = j;
}//活取最小节点
else if ((h[j].data < m2) && (h[j].tag == 0))
{
m2 = h[j].data; x2 = j;
}//获取次小节点
}
h[x1].tag = 1; h[x2].tag = 1;//标记节点已经并入树中
//计算两个节点的父节点信息
h[n + i].data = h[x1].data + h[x2].data;
h[n + i].tag = 0; h[n + i].lch = x1; h[n + i].rch = x2;
h[x1].p = n + i; h[x2].p = n + i; i++;
}
h[2 * n - 2].p = -1;//根节点
t = 2 * n - 1;
cout << "打印"<<endl;
//打印所有节点信息
for (int j = 0; j < t; j++)
{
cout << j << " " << h[j].c << " " << h[j].lch << " " << h[j].rch << " " << h[j].p << " " << h[j].tag << endl;
}
return(t);
}
四、字符串逆序函数
//将字符串逆转
void ReverseStr(char *str)
{
int i, j;
char c;
for (i = 0, j = strlen(str) - 1; i < j; i++, j--)
{
c = str[i];
str[i] = str[j];
str[j] = c;
}
}
五、根据构建好的哈夫曼树进行编码
//功能: 利用已建好的哈夫曼树,对每个字符进行编码
void EnCode()
{
int i, j, len;
for (i = 0; i <= n - 1; i++)
{
len = 0;
j = i;
cc[i].ch = h[j].c;
while (h[j].p != -1)//判断不是根节点
{
if (h[h[j].p].lch == j)//判断是否是左孩子
{
cc[i].codes[len++] = '0' + 0;
}
else//否则是右孩子
cc[i].codes[len++] = '0' + 1;
j = h[j].p;//往上遍历直到根节点
}
cc[i].codes[len] = '\0';//字符串结尾
ReverseStr(cc[i].codes);//逆序的得到哈夫曼编码
}
//进行一个打印输出
cout << "打印每个字符的哈夫曼编码:" << endl;
for (int i = 0; i < n; i++)
{
cout << cc[i].ch << " " << cc[i].codes << endl;
}
}
//对输入的字符串进行哈夫曼编码
void Encodestr()
{
int len = strlen(enterstr);
cout << "编码结果:" << endl;
for (int i = 0; i <= len - 1; i++)
{
for (int j = 0; j < n; j++)
{
if (enterstr[i] = cc[j].ch)
cout << cc[j].codes;
}
}
cout << endl;
}
六、哈夫曼译码
//功能: 利用已建好的哈夫曼树,将输入的代码进行译码
void DeCode(void)
{
int k = t - 1; //!!!根结点序号, 开始时一定在最后一个!!!
int len = 0, i = 0;
while (entercode[i])
{
if (entercode[i] == '0' + 0)
k = h[k].lch;
else if (entercode[i] == '0' + 1)
k = h[k].rch;
else {
printf("\n错误! 密文中仅能含有1和0!\n\n");
exit(-1);
}
//判断是否是叶子节点,如果是将字符放入realstr中,否则继续搜索
if (h[k].lch == -1 && h[k].rch == -1)
{
realstr[len++] = h[k].c;
k = t - 1;
}
i++;
}
realstr[len] = '\0';
//打印结果
if (k == t - 1)
{
printf("\n*** 解码结果 ***\n%s\n\n", realstr);
system("pause");
exit(0);
}
printf("\n错误! 部分密文无法解密!\n\n");
exit(-1);
}
七、 主函数
int main() {
printf("****** 哈夫曼编码与解码 ******\n\n");
hufftree();
EnCode();
cout << "输入要编码的字符串" << endl;
cin >> enterstr;
Encodestr();
cout << "输入要解码的字符串" << endl;
cin >> entercode;
DeCode();
system("pause");
return 0;
}
八、 全部C++代码实现
#include<iostream>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <ctype.h>
using namespace std;
struct huff
{
char c;//字符
int data;//权值
int lch, rch,p;//左右孩子节点及父节点在数组中的下标
int tag;//是否已并入树中
};
struct code
{
char ch;
char codes[100];
};
huff h[26];//存储叶子节点
int n;//n为叶子节点个数
int t;//节点个数
char enterstr[100];//输入字符串
char entercode[100];//输入哈夫曼编码
char realstr[100];//输出解码结果
code cc[100];
//功能: 从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树
int hufftree()
{
cout << "输入叶子节点个数" << endl;
cin >> n;//n为叶子节点个数
for (int j = 0; j < n; j++)
{
cout << "输入字符、权值";
cin >> h[j].c >> h[j].data;
h[j].lch = -1; h[j].rch = -1; h[j].tag = 0;h[j].p=-1;
}
int i = 0;
while (i < n - 1)//合并n-1次
{
int x1 = 0, m1 = 32767;//m1是最小值单元,x1为下标号
int x2 = 0, m2 = 32767;//m2是次小值单元,x2为下标号
for (int j = 0; j < n + i; j++)
{
if ((h[j].data < m1) && (h[j].tag == 0))
{
m2 = m1; x2 = x1;
m1 = h[j].data; x1 = j;
}
else if ((h[j].data < m2) && (h[j].tag == 0))
{
m2 = h[j].data; x2 = j;
}
}
h[x1].tag = 1; h[x2].tag = 1;
h[n + i].data = h[x1].data + h[x2].data;
h[n + i].tag = 0; h[n + i].lch = x1; h[n + i].rch = x2;
h[x1].p = n + i; h[x2].p = n + i; i++;
}
h[2 * n - 2].p = -1;
t = 2 * n - 1;
cout << "打印"<<endl;
for (int j = 0; j < t; j++)
{
cout << j << " " << h[j].c << " " << h[j].lch << " " << h[j].rch << " " << h[j].p << " " << h[j].tag << endl;
}
return(t);
}
//将字符串逆转
void ReverseStr(char *str)
{
int i, j;
char c;
for (i = 0, j = strlen(str) - 1; i < j; i++, j--)
{
c = str[i];
str[i] = str[j];
str[j] = c;
}
}
//功能: 利用已建好的哈夫曼树,对字符进行编码
void EnCode()
{
int i, j, len;
for (i = 0; i <= n - 1; i++)
{
len = 0;
j = i;
cc[i].ch = h[j].c;
while (h[j].p != -1)//不是根节点
{
if (h[h[j].p].lch == j)//左孩子
{
cc[i].codes[len++] = '0' + 0;
}
else//有孩子
cc[i].codes[len++] = '0' + 1;
j = h[j].p;//往上遍历
}
cc[i].codes[len] = '\0';//字符串结尾
ReverseStr(cc[i].codes);
}
cout << "打印每个字符的哈夫曼编码:" << endl;
for (int i = 0; i < n; i++)
{
cout << cc[i].ch << " " << cc[i].codes << endl;
}
}
//哈夫曼编码
void Encodestr()
{
int len = strlen(enterstr);
cout << "编码结果:" << endl;
for (int i = 0; i <= len - 1; i++)
{
for (int j = 0; j < n; j++)
{
if (enterstr[i] = cc[j].ch)
cout << cc[j].codes;
}
}
cout << endl;
}
//功能: 利用已建好的哈夫曼树,将输入的代码进行译码
void DeCode(void)
{
int k = t - 1; //根结点序号, 开始时一定在最后一个
int len = 0, i = 0;
while (entercode[i])
{
if (entercode[i] == '0' + 0)
k = h[k].lch;
else if (entercode[i] == '0' + 1)
k = h[k].rch;
else {
printf("\n错误! 密文中仅能含有1和0!\n\n");
exit(-1);
}
if (h[k].lch == -1 && h[k].rch == -1)
{
realstr[len++] = h[k].c;
k = t - 1;
}
i++;
}
realstr[len] = '\0';
if (k == t - 1)
{
printf("\n*** 解码结果 ***\n%s\n\n", realstr);
system("pause");
exit(0);
}
printf("\n错误! 部分密文无法解密!\n\n");
exit(-1);
}
int main() {
printf("****** 哈夫曼编码与解码 ******\n\n");
hufftree();
EnCode();
cout << "输入要编码的字符串" << endl;
cin >> enterstr;
Encodestr();
cout << "输入要解码的字符串" << endl;
cin >> entercode;
DeCode();
system("pause");
return 0;
}