实现思路
例如给出一组数据 [1, 3, 5, 7, 9]
构建哈夫曼树的思路
每轮循环选出最小和次小的数据来生成一个双亲结点,我们这里约定,把每轮新生成的双亲结点按顺序放在数组空的位置。
【第一轮】1 3
双亲结点为最小和次小两数之和4
此时可选择的数据为[5 7 9 4]
此时生成的树为下图
【第二轮】4 5
双亲结点为最小和次小两数之和9
此时可供下次选择的数据为[7 9 9]
此时的树为
【第三轮】7 9
双亲结点为最小和次小数之和16
此时可供下轮选择的数据为[9 16]
此时树如下图所示
【第四轮】9 16
双亲结点为最小和次小数之和25
此时已无可供下轮选择的节点,最后一个生成的双亲结点的双亲域为-1,可判边界。
下面介绍代码实现思路
代码实现构造哈夫曼树过程
这里我们重新挑选一组比较典型的数据序列[7 19 2 6 32 3 21 10]
初始表格,数组内部其他位置数据域设为无穷大,方便后面查找最小次小值。
左孩子右孩子和双亲域全部初始化为-1,代表此时并无孩子和双亲。
【第一轮循环后表格】将新生成的双亲结点按次序添加到数组新的位置
此时index++。
注意在选择最小和次小的数据的时候,只在范围为“0~index”双亲域为-1的结点当中选择,已有双亲的除外。
此时这里可供下次选择的结点剩[7 19 6 32 21]
data[8].w = data[2].w + data[5].w, data[8].lc = 2, data[8].rc = 5;
【第二轮循环后】
【第三轮】
。
。(按照此规则一直循环)
。
【最终表格】
求权值只需要深度搜索遍历每一个叶子节点(即数组中前n个数据)
递归(循环)边界:当前节点的双亲为-1,即不存在双亲结点,为根节点。
代码
#include <bits/stdc++.h>
using namespace std;
typedef struct { //节点定义
int w, p, lc, rc;
} node;
bool judge(node *data, int n) { //这里判断用的方法是:是否仅存在一个结点没有双亲,如果是,那么哈夫曼树已构建完成。
int cnt = 0;
for (int i = 0; i < n; i++) {
if (data[i].p == -1)
cnt++;
}
return (cnt == 1);
}
int get_minimum_index(node *data, int index) { //获取最小数的下标
int min_i, min = 99999;
for (int i = 0; i < index; i++) {
if (data[i].p == -1 && data[i].w <= min) {
min = data[i].w;
min_i = i;
}
}
return min_i;
}
int get_inferiormin_index(node *data, int index, int min1) { //获取次小数的下标
int min2_i, min2 = 999999;
for (int i = 0; i < index; i++) {
if (data[i].p == -1 && i != min1 && data[i].w <= min2 && data[i].w >= data[min1].w) {
min2_i = i;
min2 = data[i].w;
}
}
return min2_i;
}
void createHT(node *data, int n) { //构造哈夫曼树
int min1, min2, index = n;
for (int i = 0;; i++) {
if (judge(data, index)) //判断是否已经完成哈夫曼树的构建
break;
min1 = get_minimum_index(data, index); //获取最小的整数的下标
min2 = get_inferiormin_index(data, index, min1); //获取第二小的整数的下标
data[index].lc = min1; //令最小和次小的数据的和为他们的双亲结点,这里为新节点记录左右孩子结点的语句,一般是小的作左孩子
data[index].rc = min2;
data[min1].p = index; //记录一下最小和次小数据的双亲结点位置对应的下标
data[min2].p = index;
data[index].w = data[min1].w + data[min2].w; //数据域为最小和次小的数据域之和
data[index].p = -1; //初始化最新结点的双亲,默认-1为无双亲,方便后面判断是否已经有双亲结点
index++;
}
}
int get_minimum_len(node *data, int n) { //获取该哈夫曼树的总权值
int sum = 0, cnt = 0;
for (int i = 0; i < n; i++) { //按顺序遍历每个叶子节点,自下而上求每个叶子节点对应的权值
cnt = 0;
int t = data[i].p, now_leaf = data[i].w;
while (1) {
cnt++; //数层数
if (data[t].p == -1) { //如果达到根节点,计算完成,循环终止
sum += now_leaf * cnt;
cnt = 0;
break;
} else
t = data[t].p;
}
}
return sum;
}
int main() {
int n;
scanf("%d", &n);
node data[2 * n - 1]; //哈夫曼树的总结点数为原数据个数的两倍减一个
for (int i = 0; i < n; i++) { //初始化数组
scanf("%d", &data[i].w);
data[i].p = data[i].lc = data[i].rc = -1;
}
for (int i = n; i < 2 * n - 1; i++) {
data[i].w = 9999999;
data[i].p = data[i].rc = data[i].lc = -1;
}
createHT(data, n);
printf("%d", get_minimum_len(data, n));
}
附言
查找最小和次小值的算法还能优化