引言:
赫夫曼编码是属于一种变长编码形式。它在压缩领域有重要的运用。可以利用二叉树来构建赫夫曼树来对信号进行编码。该树的构造思想是:对于有n个叶子节点(既是需要编码的字符数)的赫夫曼树,应该有2*n-1个结点(整个赫夫曼树的总结点数)。在树进行构造的时候输入的参数是长度为2*n-1的静态链表(类型为赫夫曼树的数据类型)首指针,该静态链表的前n个位置存放叶子结点,后面的n-1个结点存放的是分支节点。
在初始化赫夫曼树时,把前面n个叶子结点的权值域与数据域用指定的值(字符数组与权值数组)进行填充,n-1个分支结点的权值域用0填充,其它域用-1进行填充。然后,在结点中寻找两个权值最小的结点node1与node2,最后用这两个权值最小的结点构造一个新的二叉树。对于已经构造了二叉树的结点,就不能在此参与构造新的二叉树了,但是这棵二叉树的根节点可以参加构造新二叉树。在初始化时,结点的双亲标志位(parent)被设置为-1,在构造二叉树时,参与构造二叉树的两个结点node1与node2的双亲标志位被设置为它们的根结点,这样它们的双亲标志位(域)就不是-1了,也就与未参与构造二叉树的结点区分开了。接下来上代码...
实现代码
赫夫曼树存储结构:
typedef char datatype; //Huffman数据类型别名
typedef struct Node2
{
datatype data; //数据域
unsigned int weight; //权值域
int parent; //父结点域
int left; //左孩子
int right; //右孩子
}HufNode, *PHufNode;<span style="font-family:Microsoft YaHei;">
</span>
赫夫曼树的构建:
/************************************************************************/
/*
初始化并且建立Huffman树
参数:weight 权值数组 n个
参数:data 数据数组 n个
参数:num 权值与数据的个数 n个
参数:hufnode huffman节点数组 n-1个
返回值:成功true 失败false
*/
/************************************************************************/
bool CHuffman::InitHufTree(const int* weight, const datatype* data, const int num, PHufNode hfnode)
{
//首先检查数据域是否为空 若为空返回
if ((nullptr==weight) || (nullptr==data) || (nullptr==hfnode))
{
std::cout << "\nthere could be a empty input pointer,please check!\n";
return false;
}
//接下来应该有weight与data数组元素个数是否相等之类的检查
//初始化huffman树
for (int i=0; i<(2*num-1); i++)
{
if (i < num) //初始化num个叶子节点
{
hfnode[i].data = data[i]; //赋值数据域
hfnode[i].weight = weight[i]; //赋值权值域
}
else //初始化num-1个分支结点
{
hfnode[i].weight = 0; //对于不是huffman叶子结点的结点的权值设置为0
}
hfnode[i].parent = -1;
hfnode[i].left = -1;
hfnode[i].right = -1;
}
unsigned int mw1(100), mw2(100); //权值计算中间变量
int node1(-1), node2(-1); //结点连接中间变量
for (int i=num; i<(2*num-1); i++)
{
mw1 = mw2 = 100;
node1 = node2 = -1;
for (int j=0; j<i; j++) //在节点(父节点域=-1)中寻找两个权值最小的节点node1和node2
{ //最后node2的权值大于node1的权值 因为node2在node1的右边
if (-1 == hfnode[j].parent)
{
if (mw1 > hfnode[j].weight)
{
mw2 = mw1;
node2 = node1;
mw1 = hfnode[j].weight;
node1 = j;
}
else if(mw2 > hfnode[j].weight)
{
mw2 = hfnode[j].weight;
node2 = j;
}
}
}
//用node1和node2构造一颗新的二叉树,二叉树根的权值为node1和node2权值的和
hfnode[i].weight = hfnode[node1].weight + hfnode[node2].weight;
hfnode[node1].parent = i;
hfnode[node2].parent = i;
//填充选出来的根节点的左右孩子域
hfnode[i].left = node1;
hfnode[i].right = node2;
}
return true;
}
获取相应单个字符的编码:
/************************************************************************/
/*
获取单个元素的Huffman编码
参数:hfnode 霍夫曼树
参数:data_pos 需要编码的霍夫曼字符 0~(N-1)
参数:N 编码的总个数
返回值:成功true 失败false
*/
/************************************************************************/
bool CHuffman::GetItemHufCode(PHufNode hfnode, const int data_pos, const int N)
{
if (nullptr == hfnode)
{
std::cout << "\nempty input error!\n";
return false;
}
if (data_pos > (N-1))
{
std::cout << "\nthe input pos is out of range!\n";
return false;
}
unsigned int parent = hfnode[data_pos].parent;
unsigned int left = data_pos;
std::cout << "\n" << hfnode[data_pos].data << ":";
while (-1 != parent) //没有达到根节点
{
if (left == hfnode[parent].left)
std::cout << "0";
else
std::cout << "1";
left = parent;
parent = hfnode[parent].parent;
}
return true;
}
赫夫曼树的层序遍历:
不是叶子结点的结点输出‘-’
void CHuffman::TravelByLevel(PHufNode hfnode, const int num)
{
std::cout << "\n";
std::vector<HufNode> vec;
vec.push_back(hfnode[2*num-2]);
unsigned int cur(0), end(1);
while (cur < vec.size())
{
end = vec.size();
while (cur < end)
{
if (NULL != vec[cur].data)
std::cout << vec[cur].data << " ";
else
std::cout << "-" << " ";
if (-1 != vec[cur].left) //若左孩子不为空将其放入向量中
{
vec.push_back(hfnode[vec[cur].left]); //压入数据
}
if (-1 != vec[cur].right) //若右孩子不为空将其放入向量中
{
vec.push_back(hfnode[vec[cur].right]); //压入数据
}
cur++;
}
std::cout << "\n";
}
}