当你开始看这篇博文的时候,我相信你对树及二叉树的基本概念已有所了解,我在这里就不再赘述。我们主要对赫
夫曼树的特点、构建、编码、译码做一个详细的介绍,并附有代码,所有函数代码都通过了测试,我不保证所有代码是最优的(毕竟是我一个人苦思冥想出来的,我相信在大家的集思广益之下还有优化的空间),但我保证所有代码是正确的。
一、赫夫曼树的特点
赫夫曼树又称作最优二叉树,是一类带权路径长度最短的树。首先给出路径和路径长度的概念。从树中一个节点到
另一个节点之间的分支构成这两个节点之间的路径,路径上的分支数目称作路径长度。树的路径长度是从树根到每一个叶子节点的路径长度之和。节点的带全路径长度为该节点到树根的路径长度乘以该节点上的权值。树的带权路径长度为树中所有叶子节点的带权路径长度之和,通常记做WPL=(w1*L1 + w2*L2 + ....+Wn * Ln)。所谓的最优二叉树就是WPL的值最小。
二、赫夫曼树的构造方法
赫夫曼最早给出了一个带有一般规律的算法,俗称赫夫曼算法,如下:
为 Wi的根节点,其左右子树都为空。①给定n个权值{w1,w2,...,wn},构成n棵二叉树的集合T={T1,T2......,Tn},其中每棵二叉树Ti只有一个权
②在T中选取两棵根节点权值最小的树做为左右子树构造一棵新的二叉树,且置新树的根节点的权值为其左右孩
子的树上的根节点权值之和。
③在T中删除这两棵树,并将新得到的二叉树加入到T中。
④重复②、③步骤,直到T中只有一个树为止。这棵树就是赫夫曼树。
构造的具体过程如下:
三、赫夫曼树构造、编码、译码的思路
在构造赫夫曼树的时候,我们需要一个链表,当给我们n个关键字及其所对应的权值的时候,先构造一个赫夫曼
树节点,然后将赫夫曼树节点做为链表节点的数据域插入到链表中(这个链表的插入操作是按照树根节点的权值大小排好序的),再构造剩余的n-1个赫夫曼树节点,并插入到链表中,当完成插入工作后,我们的有序链表也就构造出来了。然后对有序链表中的节点完成合并、删除、插入,直到链表中只有一个节点位置,这个节点的数据域就是指向赫夫曼树根节点的指针。
当有了赫夫曼树之后,我们对其进行编码,在编码的时候左分支为0,右分支为1,如下图所示。以先序遍历二叉
树,然后还要定义一整数a,初始值为1。在访问某一节点时将其作为参数传入,如果访问的是左节点传入a*2,访问右节点传入a*2+1,也就是在a的二进制数据中,向左走在末尾加个0,向右走加个1(初始化为1是为了避免开始时向左走,无法加零的情况,输出时要将首位的1去掉),访问到叶子节点时将a转化成二进制,再将首位的1去掉即可。
孩子 往下走;直到走到叶子节点为止。如果报文中还有数据则接着从赫夫曼树的根节点出发查找。如果在查找的过程中无法找到叶子节点,则报文有错误。赫夫曼译码:我们从报文中取出二进制,如果为0,则沿着根节点的左孩子往下走;如果为1,则沿着根节点的右
四、赫夫曼树构造、编码、译码代码如下
1、我们需要两个结构体,一个是赫夫曼树节点的结构体,另一个是链表节点的结构体:
typedef struct Node
{
int weight;
key_type key;
struct Node *lchild, *rchild;
}HFMNode,*HFMTree;
/*用于链表的结构体*/
typedef struct link_node
{
HFMTree data;
struct link_node *next;
}LNode,*Link;
2、用到的函数如下:
void init_link(Link *head);//初始化链表
void insert_link(Link head, HFMTree hfm);//向链表中插入一个元素,并按照权重排序
int delete_link(Link head,HFMTree *hfm);//依次删除链表中的数据,成功返回1,失败返回0
/*创建赫夫曼树,str为关键字,w为对应的权重*/
int creat_hfmTree(HFMTree *root,char str[],int w[]);
/*获取赫夫曼编码表,存储在数组code中*/
void hfmTree_code(HFMTree head, int a,char code[]);
/*译码,译码结果存储在decode数组中,code输入的报文*/
int hfmTree_decode(HFMTree head,const char code[],char decode[]);
3、整体代码如下:
#include <iostream>
#include <assert.h>
typedef char key_type;
/*用于赫夫曼树的结构体*/
using namespace std;
typedef struct Node
{
int weight;
key_type key;
struct Node *lchild, *rchild;
}HFMNode,*HFMTree;
/*用于链表的结构体*/
typedef struct link_node
{
HFMTree data;
struct link_node *next;
}LNode,*Link;
/*对链表的操作*/
void init_link(Link *head);//初始化链表
void insert_link(Link head, HFMTree hfm);//向链表中插入一个元素,并按照权重排序
int delete_link(Link head,HFMTree *hfm);//依次删除链表中的数据,成功返回1,失败返回0
/*创建赫夫曼树,str为关键字,w为对应的权重*/
int creat_hfmTree(HFMTree *root,char str[],int w[]);
/*获取赫夫曼编码表,存储在数组code中*/
void hfmTree_code(HFMTree head, int a,char code[]);
/*译码,译码结果存储在decode数组中,code输入的报文*/
int hfmTree_decode(HFMTree head,const char code[],char decode[]);
int main()
{
HFMTree root;
char str[] = "abcdefg";
int w[] = { 12, 18, 36, 79,85,32,40};
creat_hfmTree(&root, str, w);
char code[1024] = { 0 };//用来存放编码表
hfmTree_code(root, 1,code);
cout <<"编码结果为:"<<endl<< code << endl;
char decode[90] = { 0 };
hfmTree_decode(root, "00001100010010100111011", decode);
cout <<"译码结果为:"<<endl<< decode << endl;
}
void init_link(Link *head)
{
(*head) = (Link)malloc(sizeof(LNode));
(*head)->next = NULL;
(*head)->data = NULL;
}
void insert_link(Link head, HFMTree hfm)
{
assert(head != NULL);
/*先构造链表节点*/
Link temp = (Link)malloc(sizeof(LNode));
temp->next = NULL;
temp->data = hfm;
while (head->next != NULL && head->next->data->weight < hfm->weight)
{
head = head->next;
}
if (head->next == NULL)
{
head->next = temp;
}
else
{
temp->next = head->next;
head->next = temp;
}
}
int delete_link(Link head, HFMTree *hfm)
{
assert(head != NULL);
if (head->next == NULL)
return 0;
Link temp = head->next;
head->next = temp->next;
*hfm = temp->data;
free(temp);
return 1;
}
int creat_hfmTree(HFMTree *root, char str[], int w[])
{
int n = strlen(str);
HFMTree temp;
Link head;
init_link(&head);
/*构造节点,按照权重的递增顺序插入到链表中*/
for (int i = 0; i < n;i++)
{
/*构造节点*/
temp = (HFMTree)malloc(sizeof(HFMNode));
temp->key = str[i];
temp->weight = w[i];
temp->lchild = NULL;
temp->rchild = NULL;
insert_link(head, temp);
}
if (head->next == NULL)//链表中没有元素
return 0;
/*从链表中取出两个最小的节点构造赫夫曼树*/
HFMTree temp1, temp2, temp3;
/*链表中存在1个元素则退出循环*/
while ( delete_link(head,&temp1) && delete_link(head,&temp2) )
{
temp3 = (HFMTree)malloc(sizeof(HFMNode));
temp3->weight = temp1->weight + temp2->weight;
temp3->lchild = temp1;
temp3->rchild = temp2;
insert_link(head, temp3);
}
*root = temp1;
return 1;
}
void hfmTree_code(HFMTree head, int a, char code[])
{
static int i = 0;
if (head != NULL)
{
if (head->lchild == NULL && head->rchild == NULL)//访问到了叶子节点
{
code[i++] = head->key;
code[i++] = ':';//在关键字后面加":",其后面的"0"、"1"表示编码
int count = 0;
/*对a做处理,a最前面的1要舍去*/
while (a > 1)
{
if (a % 2 == 0)
code[i++] = '0';
else
code[i++] = '1';
a = a>>1;
count++;
}
/*对字符串反转,这样才和我们的赫夫曼编码一直*/
strrev( code + i - count);
/*关键字之间的编码用空格分开*/
code[i++] = ' ';
}
hfmTree_code(head->lchild, a * 2, code);
hfmTree_code(head->rchild, a * 2 + 1, code);
}
}
int hfmTree_decode(HFMTree head, const char code[], char decode[])
{
assert(head != NULL);
HFMTree root = head,pre;
int i = 0;
int j = 0;
while (code[i] == '0' || code[i] == '1')
{
if (code[i] == '0')
head = head->lchild;
else
head = head->rchild;
if (head->rchild == NULL )//访问到了叶子节点
{
decode[j] = head->key;
j++;
head = root;
}
i++;
}
if (code[i] != '\0')//没有访问到叶子节点
{
cout << "报文错误" << endl;
return 0;
}
return 1;
}
输出结果为: