赫夫曼树的基本概念
在认识赫夫曼树之前,我们需要弄清以下的相关概念、
1、路径
路径就是从某一个结点往下到达另一个结点所走的通路
2、路径长度
路径长度就是某一路径所经过的边的数量(两个结点之间的连线)
3、结点的带权路径长度
若给每一个结点分配一个权重,则该结点的带权路径长度等于
(路径长度)* 权重, 此时路径长度指从根结点走到该结点
4、树的带权路径长度
树的带权路径长度为所有叶子结点的带权路径长度之和
5、哈夫曼树
给定n个权值赋给n个叶子结点,构建一个二叉树,使得树的带权路径长度最小的树称为哈夫曼树,也成为最优二叉树
例如A的带权路径长度便为:3 * 5 = 15
所以由上我们可以知道,不同二叉树的带权路径长度与二叉树的构型(即叶子结点的分布)有关。
根据树的带权路径长度的计算规则,我们应该尽可能地让权值大的叶子结点靠近根结点,让权值小的叶子结点远离根结点,这样便能使得这棵二叉树的带权路径长度达到最小。
哈夫曼树的构建
构建思路:
1、初始状态下有n个结点,每个结点有一个权值。
2、在所有结点中找到权值最小的两棵树,生成他们的父结点,权值为这两个结点的权值之和,相当于看成一颗树
3、重复操作2,直到最后只剩下一棵树,即为哈夫曼树
代码实现:
数组下标为0的地方不储存数据!!
typedef double ElemType;
typedef struct HTNode{ //结点数据类型
ElemType weight; //权值
int parent; //父结点
int lc, rc; //左孩子和右孩子
} *HuffTree;
//在下标1到i-1中找到权重值最小的两个下标,以引用传递的方式返回,
//其中s1的权值小于s2的权值
void Select(HuffTree &HT, int n, int &s1, int &s2)
{
int min;
for(int i=1; i<=n; i++)
{
if(HT[i].parent == 0)
min = i;
break;
}
//找第一个最小值
for(int i=min+1; i<=n; i++)
{
if(HT[i].parent == 0 && HT[i].weight < HT[min].weight)
{
min = i;
}
}
s1 = min;
找第二个最小值
for(int i=1; i<=n; i++)
{
if(HT[i].parent == 0)
min = i;
break;
}
//找第一个最小值
for(int i=min+1; i<=n; i++)
{
if(HT[i].parent == 0 && HT[i].weight < HT[min].weight && i != s1)
{
min = i;
}
}
s2 = i;
}
//传入n个数据来创建创建哈夫曼树, 初始状态,所有的
void CreateHuff(HuffTree &HT, ElemType *w, int n)
{
int m = 2*n - 1; //哈夫曼树的结点总数;
HT = (HuffTree)malloc(sizeof(HTNode) * (m+1));
//申请m+1个因为数组零下标不存放数据
for(int i=1; i<=n; i++)
HT[i].weight = w[i-1];
//构建哈夫曼树
for(int i=n+1; i<=m; i++)
{
//选择权值最小的w1和w2,生成他们的父结点
int w1, w2;
Select(HT, i-1, w1, w2);
HT[i].weight = HT[w1].weight + HT[w2].weight;
HT[w1].parent = i;
HT[w2].parent = i;
HT[i].lc = w1;
HT[i].rc = w2;
}
//打印测试代码;
printf("哈夫曼树为:>\n");
printf("下标 权值 父结点 左孩子 右孩子\n");
printf("0 \n");
for (int i = 1; i <= m; i++)
{
printf("%-4d %-6.2lf %-6d %-6d %-6d\n", i, HT[i].weight, HT[i].parent, HT[i].lc, HT[i].rc);
}
printf("\n");
}
哈夫曼编码
编码生成思路
对于任意一颗二叉树来说,把左分支记为0,右分支记为1,那么从根结点到该结点的路径自然就组成了一串数字
具体形成的编码表如下图:
具体代码:
typedef char ** HuffTreeCode;
void HuffmanCode(HuffTree &HT, HuffTreeCode &code,int n){
HC = (HuffTreeCode) malloc(sizeof(char *)*(n + 1));//下标为0的空间不用,所以开n+1个空间
//HuffTreeCode为char **类型,即字符指针数组
char *code = (char *)malloc(sizeof(char)*n);
code[n-1] = '\0';//辅助存储空间最后一个位置为0
for(int i=1; i<=n; i++)
{
int start = n-1;//每次将start指向'\0'的位置
int temp = i;//正在进行第i个数据编码
int p = HT[i].parent;
while(p) //根结点的parent为0
{
if(HT.lc == i)
code[--start] = 1;
else if(HT.rc == i)
code[--start] = 0;
temp = p;
p = HT[temp].parent;
}
HC[i] = (char)malloc(sizeof(char)*(n+1));//开n+1个空间是因为斜树最长情况下字符数组中为n个1,还有字符串结尾标志
strcpy(HC[i], code);
}
free(code);
}
本文转载借鉴:https://blog.csdn.net/chenlong_cxy/article/details/117929139?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164534775416781683919602%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=164534775416781683919602&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_click~default-2-117929139.pc_search_result_positive&utm_term=%E5%93%88%E5%A4%AB%E6%9B%BC%E6%A0%91&spm=1018.2226.3001.4187