哈弗曼树的建立、编码与部分代码解释
今天就来回顾一下二叉树,其实在大二上专业课的时候就做过一次哈弗曼树的编码了,这次只做简单的复习,因此忽略掉其中对文本的转换,只实现了哈弗曼树的构建和节点编码。
在哈弗曼树构建的过程中,一个很重要的点就是这个树的规模到底有多大。
对于完全二叉树有性质:
\[n0 = n2+1;
\]
其中n0指的是叶子节点,即儿子节点的个数为0,n2指的是有两个儿子节点的节点。
而对于哈弗曼树,它是由每两个节点得到一个新节点逐渐构建而成的,因此只有节点数为0或者为2的节点,而且我们需要编码的字符一定都是叶子节点。因此,如果我们需要编码的字符有n个,即叶子节点的个数n0=n,我们可以推得:
\[\begin{align}
&n0 + n1 + n2 \\ &= n + 0 + n - 1 \\&= 2 * n -1;
\end{align}
\]
因此我们可以知道我们要开的存放树的节点空间要多大。
字符编码的时候要注意主要思想是从叶子节点往根节点走,因此我们在设计结构体的时候需要有存放parent的下标的变量。这样子在查找编码的过程中,只需要从叶子节点开始,走到根节点即可。
其他的不详细解释,大概的流程是:
- 先对所有的元素做初始化,动态数组中的前n个元素存放着我们需要编码的字符及对应信息,因此可以先对这些节点的weight或者data赋值。
- 动态数组中n+1到2n-1的元素就是我们在哈弗曼树构建过程中新增的节点
- 找到数据中的前两个权重最小的元素,并创建一个新的节点,对这个节点赋值
- 字符编码
具体实现代码如下:
#include<iostream>
#include<vector>
#include<string>
#define M 65535
using namespace std;
typedef struct HuffTreeNode{
int left;
int right;
int weight;
int parent;
}HuffTreeNode, *HuffTreeP;
typedef struct HuffTreeCode{
int data;
char* code;
}HuffTreeCode, *HuffTreeCodeP;
void buildTree(HuffTreeP & HuffTree, int n){
int m , t, i;
//int arr[7] = {1,2,4,5,6,7,9};
//n0 = n, 共有2n-1个节点
m = 2 * n - 1;
HuffTree = (HuffTreeP)malloc((2 * n - 1)* sizeof(HuffTreeNode));
// init leaf node
for(i = 0; i < n; i++){
cin >> t;
//t = arr[i];
HuffTree[i].parent = -1;
HuffTree[i].right = -1;
HuffTree[i].left = -1;
HuffTree[i].weight = t;
}
// init other nodes
for(i = n; i < m; i++){
HuffTree[i].parent = -1;
HuffTree[i].right = -1;
HuffTree[i].left = -1;
HuffTree[i].weight = M;
}
int min1, min2 , index1 = -1, index2 = -1;
for(int i = n; i < m; i++){
//find two biggest value
min1 = M;
min2 = min1;
for(int j = 0; j < m ; j++){
if(HuffTree[j].parent != -1){
continue;
}
if(HuffTree[j].weight <= min1 && HuffTree[j].weight < min2){
min2 = min1;
min1 = HuffTree[j].weight;
index2 = index1;
index1 = j;
}else if(HuffTree[j].weight >= min1 && HuffTree[j].weight < min2){
min2 = HuffTree[j].weight;
index2 = j;
}
}
//构建这两个子节点的父节点
HuffTree[index1].parent = i;
HuffTree[index2].parent = i;
HuffTree[i].left = index1;
HuffTree[i].right = index2;
HuffTree[i].weight = min1 + min2;
cout << "min1=" << min1 << " min2=" << min2 << endl;
}
}
void reverse(char* & s, int m){
char* sTemp = (char * )malloc((m+1) * sizeof(char));
int i = 0;
cout << "s=" << s << endl;
while(m >= 0){
sTemp[i++] = s[m--];
}
s = sTemp;
}
void encode(HuffTreeP HuffTree, HuffTreeCodeP & codeTree, int n){
//parent = -1为根节点
codeTree = (HuffTreeCodeP)malloc(n* sizeof(HuffTreeCode));
char * s = (char*)malloc((n+1) * sizeof(char));
//string s = "";
int j = 0, p = 0, m = 0;
for(int i = 0; i < n; i++){
m = 0;
j = i;
while(j < 2 * n - 2){
p = HuffTree[j].parent;
if(HuffTree[p].left == j){
s[m]= '0';
}else{
s[m]= '1';
}
j = p;
m++;
}
cout << "data=" << codeTree[i].data << " code=" << s << endl;
reverse(s, m);
codeTree[i].code = s;
codeTree[i].data = HuffTree[i].weight;
}
}
void printCode(HuffTreeCodeP codeTree, int n){
for(int i = 0; i < n; i++){
cout << "data: " << codeTree[i].data << " code: " << codeTree[i].code << endl;
}
}
int main()
{
int n;
cin >> n;
// n = 7;
HuffTreeP HuffTree;
HuffTreeCodeP codeTree;
buildTree(HuffTree, n);
encode(HuffTree, codeTree, n);
printCode(codeTree, n);
}