哈夫曼编码描述:
哈夫曼编码利用字符的使用频率来编码,采用不定长的编码方法,使经常使用的字符编码较短,而不经常使用的字符编码较长;
哈夫曼编码有两个特点:
1、编码尽可能短,使用频率越高,编码越短;
2、编码不能有二义性,一个字符的编码不能是另一个字符编码的前缀。
哈夫曼树的构建:
1、首先定义结点类,每一个结点有5个成员:权值(使用频率)、父亲、左孩子、右孩子、本身的值;
class Node
{
public:
double weight; //权值
int father;
int lchile;
int rchile;
char value; //结点本身的值
};
2、建立结点类型的数组,若字符的个数最大为N,共需要存放2N-1个元素
Node huff_node[MAX_NODE * 2 - 1]; //算上新生成的结点,需要存储2n-1个结点
3、构建树:
首先,将所有2n-1个结点对象全部初始化,父亲和左右孩子用-1,然后根据输入,保存前n个结点的权值和代表的字符;
之后,每次在当前没有父亲的结点中选出两个权值最小的结点,权值最小的结点作为左孩子,权值次小的结点作为右孩子,构建一棵新树。
父亲的权值为左右孩子的权值之和,每次构建新树需要更改5个成员:左右孩子的父亲、父亲的左右孩子,父亲权值;共需要构建n-1次(n-1个新生成的结点)。
void CreateHuffmanTree(Node* node,const int& n)
{
double m1 = 0, m2 = 0; //用于存放最小权值和次小权值
int x1 = 0, x2 = 0;
for (int i = 0; i < 2 * n - 1; i++)
{
node[i].weight = 0;
node[i].father = -1;
node[i].lchile = -1;
node[i].rchile = -1;
}
for (int i = 0; i < n; i++)
{
cout << "请输入第" << i + 1 << "个结点以及权值:" << endl;
cin >> node[i].value >> node[i].weight;
}
for (int i = 0; i < n - 1; i++) //要构建剩下的n-1个结点
{
m1 = m2 = 0x7fffffff;
x1 = x2 = -1;
for (int j = 0; j < i + n; j++) //在已经构建好的i+n个结点中找出权值最小的两个孤儿结点
{
if (node[j].weight < m1 && node[j].father == -1)
{
m2 = m1;
x2 = x1;
m1 = node[j].weight;
x1 = j;
}
else if (node[j].weight < m2 && node[j].father == -1)
{
m2 = node[j].weight;
x2 = j;
}
}
node[x1].father = n + i;
node[x2].father = n + i;
node[n + i].lchile = x1;
node[n + i].rchile = x2;
node[n + i].weight = m1 + m2;
}
}
根据哈夫曼树获取字符哈夫曼编码 :
1、建立编码类:一个数组类型成员,用于存放一个字符的哈夫曼编码;一个整形变量,用于记录开始读的位置;由于在哈夫曼树中,需要编码的结点都是叶子,适合自底向上,数组从尾部开始插入元素。
class Code
{
public:
int code[MAX_NODE]; //存储结点的哈夫曼编码
int start; //开始的读的下标
};
2、建立编码类型数组,若输入字符最大个数为N,共N个元素
Code huff_code[MAX_NODE];
3、获取哈夫曼编码
定义两个变量c和f,c为当前需要编码的结点下标,f为c的父亲;
若c为f的左结点,对应字符编码数组的开始位置插入0,反之插入1;然后将编码数组开始位置前移1位,c和f向上回溯一层;重复此过程直至回溯到根,即可求得c代表字符的编码;
将上述过程重复n次,即可获取n个字符的哈夫曼编码
void GetHuffmanCode(Code* code, const int& n)
{
int c, f;
for (int i = 0; i < n; i++)
{
huff_code[i].start = n - 1;
c = i;
f = huff_node[c].father;
while (f != -1)
{
if (huff_node[f].lchile == c)
{
huff_code[i].code[huff_code[i].start] = 0;
}
else
{
huff_code[i].code[huff_code[i].start] = 1;
}
huff_code[i].start--;
c = f;
f = huff_node[f].father;
}
}
}
总体代码实现:
#include <iostream>
using namespace std;
#define MAX_NODE 100
//结点类
class Node
{
public:
double weight; //权值
int father;
int lchile;
int rchile;
char value; //结点本身的值
};
//编码类
class Code
{
public:
int code[MAX_NODE]; //存储结点的哈夫曼编码
int start; //开始的读的下标
};
Node huff_node[MAX_NODE * 2 - 1]; //算上新生成的结点,需要存储2n-1个结点
Code huff_code[MAX_NODE];
//构造哈夫曼树
void CreateHuffmanTree(Node* node,const int& n)
{
double m1 = 0, m2 = 0; //用于存放最小权值和次小权值
int x1 = 0, x2 = 0;
for (int i = 0; i < 2 * n - 1; i++)
{
node[i].weight = 0;
node[i].father = -1;
node[i].lchile = -1;
node[i].rchile = -1;
}
for (int i = 0; i < n; i++)
{
cout << "请输入第" << i + 1 << "个结点以及权值:" << endl;
cin >> node[i].value >> node[i].weight;
}
for (int i = 0; i < n - 1; i++) //要构建剩下的n-1个结点
{
m1 = m2 = 0x7fffffff;
x1 = x2 = -1;
for (int j = 0; j < i + n; j++) //在已经构建好的i+n个结点中找出权值最小的两个孤儿结点
{
if (node[j].weight < m1 && node[j].father == -1)
{
m2 = m1;
x2 = x1;
m1 = node[j].weight;
x1 = j;
}
else if (node[j].weight < m2 && node[j].father == -1)
{
m2 = node[j].weight;
x2 = j;
}
}
node[x1].father = n + i;
node[x2].father = n + i;
node[n + i].lchile = x1;
node[n + i].rchile = x2;
node[n + i].weight = m1 + m2;
}
}
//获取哈夫曼编码
void GetHuffmanCode(Code* code, const int& n)
{
int c, f;
for (int i = 0; i < n; i++)
{
huff_code[i].start = n - 1;
c = i;
f = huff_node[c].father;
while (f != -1)
{
if (huff_node[f].lchile == c)
{
huff_code[i].code[huff_code[i].start] = 0;
}
else
{
huff_code[i].code[huff_code[i].start] = 1;
}
huff_code[i].start--;
c = f;
f = huff_node[f].father;
}
}
}
//输出哈夫曼编码
void OutputHuffmanCode(const int& n)
{
for (int i = 0; i < n; i++)
{
cout << huff_node[i].value << ": ";
for (int j = huff_code[i].start + 1; j < n; j++)
{
cout << huff_code[i].code[j];
}
cout << endl;
}
}
int main()
{
int n;
cout << "请输入结点个数:" << endl;
cin >> n;
CreateHuffmanTree(huff_node, n);
GetHuffmanCode(huff_code, n);
OutputHuffmanCode(n);
return 0;
}