在计算机中每个西文字符是用一个编码表示,大多数编码系统都采用等长编码,如ASCII编码
例如在某段文本中用到了下列字符,括号中是它们出现的频率:a(10), e(15), i(12), s(3), t(4), 空格(13), 换行(1)。如采用定长编码,7个不同的字符至少要用3位编码。
下图给出了一种编码对应方法:
上述编码形式可以对应下面的二叉树,左支为0,右枝为1
很显然,将换行上移一层可以减少存储量
不等长编码可以减少存储量
两种编码方式对比:
右侧所对应的编码方式:
如何构造这个使所用存储空间最小的编码,便引出了哈夫曼树的概念
哈夫曼树是一棵最小代价的二叉树,在这棵树上,所有的字符都包含在叶结点上,要使得整棵树的代价最小,显然权值大的叶子应当尽量靠近树根,权值小的叶子可以适当离树根远一些。
一些相关概念定义:
构建方法:
存储方法:
编码的产生:
对每个结点,从叶子往根推进,是左枝加0,是右枝加1
template<class Type>
class hfTree {
private:
struct Node {
Type data;//节点值
int weight;//节点的权值
int parent, left, right;//父节点下标,左右孩子下标
};
Node* elem;
int length;//数组长度
public:
struct hfCode {
Type data;
string code;//哈夫曼编码
};
hfTree(const Type* x, const int* w, int size);
void getCode(hfCode result[]);
~hfTree() {
delete[]elem;
}
};
//构造函数
template<class Type>
hfTree<Type>::hfTree(const Type* v, const int* w, int size) {
const int MAX_INT = 32767;
int min1, min2;//最小树、次小数的权值
int x, y;//最小树、次最小树的下标
//置初值
length = 2 * size;//数组长度
elem = new Node[length];
for (int i = size; i < length; ++i) {
elem[i].weight = w[i - size];
elem[i].data = v[i - size];
elem[i].parent = elem[i].left = elem[i].right = 0;
}
//构造新的二叉树
for (int i = size - 1; i > 0; --i) {
min1 = min2 = MAX_INT;
x = y = 0;
for (int j = i + 1; j < length; ++j) {
if (elem[j].parent == 0)
if (elem[j].weight < min1) {//如果有元素的权值比当前最小值小,则更新次小值和最小值
min2 = min1;
min1 = elem[j].weight;
y = x;
x = j;
}
else if (elem[j].weight < min2) {//元素的权值大于当前最小值但小于次小值,更新次小值
min2 = elem[j].weight;
y = j;
}
}
elem[i].weight = min1 + min2;
elem[i].left = y;//次小值(作为左子)的下标
elem[i].right = x;//最小值的下标
elem[i].parent = 0;
elem[x].parent = i;
elem[y].parent = i;
}
}
getCode(hfCode<Type> result[]) //求哈夫曼编码的伪代码
{
for (int i = size; i < length; ++i)
{ result[i - size].data = elem[i].data;
result[i - size].code = "";
p = elem[i].parent; s = i;
while (p不等于0) {
if (p的左孩子是 == s) result[i - size].code 前添加‘0’;
else result[i - size].code 前添‘1’;
移到上一层;
}
}
}
代码如下:
//求哈夫曼编码
template<class Type>
void hfTree<Type>::getCode(hfCode result[]) {
int size = length / 2;
int p, s;//s是正在处理的节点,p是s的父节点下标
for (int i = size; i < length; ++i) {
result[i - size].data = elem[i].data;
result[i - size].code = "";
p = elem[i].parent;
s = i;
while (p) {//即当p!=0时
if (elem[p].left == s) {//如果s所对应的数为p的左子,则加0
result[i - size].code = '0' + result[i - size].code;
}
else result[i - size].code = '1' + result[i - size].code;
s = p;
p = elem[p].parent;
}
}
#include
using namespace std;
template<class Type>
class hfTree {
private:
struct Node {
Type data;//节点值
int weight;//节点的权值
int parent, left, right;//父节点下标,左右孩子下标
};
Node* elem;
int length;//数组长度
public:
struct hfCode {
Type data;
string code;//哈夫曼编码
};
hfTree(const Type* x, const int* w, int size);
void getCode(hfCode result[]);
~hfTree() {
delete[]elem;
}
};
//构造函数
template<class Type>
hfTree<Type>::hfTree(const Type* v, const int* w, int size) {
const int MAX_INT = 32767;
int min1, min2;//最小树、次小数的权值
int x, y;//最小树、次最小树的下标
//置初值
length = 2 * size;//数组长度
elem = new Node[length];
for (int i = size; i < length; ++i) {
elem[i].weight = w[i - size];
elem[i].data = v[i - size];
elem[i].parent = elem[i].left = elem[i].right = 0;
}
//构造新的二叉树
for (int i = size - 1; i > 0; --i) {
min1 = min2 = MAX_INT;
x = y = 0;
for (int j = i + 1; j < length; ++j) {
if (elem[j].parent == 0)
if (elem[j].weight < min1) {//如果有元素的权值比当前最小值小,则更新次小值和最小值
min2 = min1;
min1 = elem[j].weight;
y = x;
x = j;
}
else if (elem[j].weight < min2) {//元素的权值大于当前最小值但小于次小值,更新次小值
min2 = elem[j].weight;
y = j;
}
}
elem[i].weight = min1 + min2;
elem[i].left = y;//次小值(作为左子)的下标
elem[i].right = x;//最小值的下标
elem[i].parent = 0;
elem[x].parent = i;
elem[y].parent = i;
}
}
//求哈夫曼编码
template<class Type>
void hfTree<Type>::getCode(hfCode result[]) {
int size = length / 2;
int p, s;//s是正在处理的节点,p是s的父节点下标
for (int i = size; i < length; ++i) {
result[i - size].data = elem[i].data;
result[i - size].code = "";
p = elem[i].parent;
s = i;
while (p) {//即当p!=0时
if (elem[p].left == s) {//如果s所对应的数为p的左子,则加0
result[i - size].code = '0' + result[i - size].code;
}
else result[i - size].code = '1' + result[i - size].code;
s = p;
p = elem[p].parent;
}
}
}
#include
#include"hfTree.h"
using namespace std;
int main()
{
char ch[] = { "aeistdn" };
int w[] = { 10,15,12,3,4,13,1 };
hfTree<char> tree(ch, w, 7);
hfTree<char>::hfCode result[7];
tree.getCode(result);
for (int i = 0; i < 7; ++i)
cout << result[i].data << ' '
<< result[i].code << endl;
return 0;
}