基本概念:
1路径:
树中一个结点到另一个结点之间的分支构成这两个结点间的路径
2路径长度:
两结点间路径的分支数
图像展现:

3树的路径长度:
每一个结点的路径长度之和
4权:
将树中结点赋予一个有着某种含义的数值,这个数值称为权
5带权路径长度:
从根结点到该结点间路径长度与该结点权的乘积
6树的带权路径长度:
树中所有叶子结点的带权路径长度之和
7哈夫曼树:
带权路径最短的二叉树(带权路径最短需要在相同的树中比较)
则哈夫曼树中权越大的叶子离根越近
8哈夫曼算法(构造哈夫曼树的方法):
(1)根据n个给定的权值以每个权值作为根结点的权值构造n棵二叉树森林,每个树只有一个结点
(2)选取两棵根结点权值最小的树作为左右子树,构造一棵新的二叉树,新二叉树根结点权值为左右子树权值之和
(3)删除构成新子树的两棵子树,将新子树加入森林中
(4)重复(2)(3),直到得到最后一棵树,这棵树就是哈夫曼树
口诀:构造森林全是根,选用两小造新树,删除两小添新人,重复二三剩单根

构造哈夫曼树:
原理:
用数组来储存哈夫曼树中各结点的权值,双亲,左右孩子的位置(在数组中的序号)


代码实现:
结点结构:
typedef struct
{
int weight;
int parent, lchild, rchild;
}HTNode, * HuffmanTree;
哈夫曼树结构:
void Lowest(HuffmanTree HT, int k/*在数组k之前的数据中比较*/, int &a, int &b/*找到两个权重最小的结点序号存在a,b中*/)
{
int i;
for (i = 1; i <= k; i++)
if (HT[i].parent == 0)
{
a = i;break;
}
for (i = 1; i <= k; i++)
if (HT[i].parent == 0 && HT[a].weight > HT[i].weight)
{
a = i;
}
for (i = 1; i <= k; i++)
if (HT[i].parent == 0 && i != a)
{
b = i;break;
}
for (i = 1; i <= k; i++)
if (HT[i].parent == 0 && HT[b].weight > HT[i].weight && i != a)
{
b = i;
}
}
void CreateHuffmanTree(HuffmanTree &HT, int n/*叶子结点数*/)/*构造哈夫曼树*/
{
/*初始化哈夫曼树*/
int m, i;
if (n <= 1)return;
m = n * 2 - 1;/*哈夫曼树共有2n-1个结点*/
HT = new HTNode[m + 1];
for (i = 1; i <= m; i++)
{
HT[i].parent = HT[i].lchild = HT[i].rchild = 0;
}
for (i = 1; i <= n; i++)cin >> HT[i].weight;/*输入元素权重*/
/*构造*/
int s1, s2;
for (i = n + 1; i <= m; i++)
{
Lowest(HT, i - 1, s1,s2);
HT[s1].parent = HT[s2].parent = i;
HT[i].lchild = s1;
HT[i].rchild = s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;
}
}
typedef char** HuffmanCode;
哈夫曼树编码:
原理:
1,统计字符集中每个字符出现的平均概率,概率越大,编码应越短
2,利用哈夫曼树的特点:权越大的叶子离根越近:将每个字符的概率值作为权值,构造哈夫曼树
3,哈夫曼树结点的左分支标0,右分支标1;将根到每个叶子的路径上的标号连接起来,作为该叶子代表的字符的编码
代码实现:
void CreateHuffmanTreeCode(HuffmanTree HT, HuffmanCode& HC, int n)
{
int i,start,c,f;
char *cd =new char[n];;
HC=new char *[n+1];
cd[n - 1] = '\0';
for (i = 1; i <= n; i++)
{
start = n - 1;
c = i;
f = HT[c].parent;
while (f != 0)
{
start--;
if (HT[f].lchild == c)cd[start] = '0';
else cd[start] ='1';
c = f;
f = HT[f].parent;
}
HC[i] = new char[n - start];
strcpy_s(HC[i],strlen(cd) +1, & cd[start]);
}
delete []cd;
}
结果展示:
数据1:

数据2:

数据3:


错误总结:
1strcpy(a,b)
老版本其功能是将b数组复制给a,返回a
vs中不支持strcpy(a,b)函数,编译时会出错

因为如果b数组比a数组大,就会发生错误;应使用strcpy_s(a,n,b),n为中间缓冲区空间,即将b复制到a所需中间储存的内存,一般用strlen(b)+1,可避免缓冲区空间溢出
2动态分配数组空间
声明:如需声明a[n];n为变量,则int a[n]是错误的,该形式只能声明静态常量数组,应该用int new a[n]来定义;
删除:静态数组可用delete来删除,但删除动态数组时会发生错误,系统提示如下

应使用delete[]来删除,如delete [] a;来删除动态数组a[n]
总结:
1哈夫曼树优点:哈夫曼树又称最优二叉树,是带权路径长度最短的二叉树,可用于编码
缺点:哈夫曼编码若用于通讯网络,则延迟较大;对于较短的数据编码意义不大,对于较长数 据,频繁的磁盘访问会降低数据编码速度