C++ 数据结构 构造Huffman哈夫曼树 求权值

实现思路

例如给出一组数据 [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));
}

附言

查找最小和次小值的算法还能优化

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值