【树】哈夫曼树和哈夫曼编码

news2024/9/24 15:27:23

哈夫曼(Huffman)树,又称最优树,是一类带权路径长度最短的树。

最优二叉树(哈夫曼树)

路径:从树中一个结点到另一个结点之间的分支构成这两个结点之间的路。

路径长度:路径上的分支数目;

树的路径长度:树根到每一结点的路径长度之和。

在结点数目相同的二叉树中,完全二叉树的路径长度最短。

考虑带权的结点

树的带权路径长度WPL为树中所有叶子结点的带权路径长度之和:

带权路径长度最小的二叉树称为最优二叉树。

如下图:计算出第三棵树的WPL最小,所以它为哈夫曼树


建立哈夫曼树的核心是贪心算法:选权值小的树

步骤:

1.根据给定的n个权值构成n棵二叉树,并加入集合F,这n个二叉树只有一个根结点,没有左右子树;

2.选择集合F中根结点权值最小的两棵树作为左右子树构造一棵新二叉树,且新二叉树的根结点的权值为其左右子树上根结点的权值之和;

3.在F中删除这两棵树,加入新得到的二叉树;

4.重复2,3直到F只含一棵树,这棵树便是哈夫曼树;

如下图:(小左大右)

权值越小离根越远,权值越大离根越近

因为要选择根值最小的两个树,所以这个集合中存根的地方用优先级队列,方便出队选择

树的结点结构:

typedef unsigned int WeigthType;
typedef unsigned int NodeType;
typedef struct//结点结构
{
    WeigthType weigth;
    NodeType parent, leftchild, rightchild;
}HTNode;
typedef HTNode HuffmanTree[m];

初始化

void InitHuffManTree(HuffmanTree hft,WeigthType w[])
{
    memset(hft, 0, sizeof(HuffmanTree));
    for (int i = 0; i < n; i++)
    {
        hft[i + 1].weigth = w[i];//0下标不用,所以+1
    }
//printf("HuffManTree:size%d\n", sizeof(HuffmanTree));//16*16=256(数组类型m(6)个*结构体大小(16))
//printf("hft:        size%d\n", sizeof(hft));//4,数组类型在这当指针
//printf("w:          size%d\n", sizeof(w));//4,指针
}

构建哈夫曼树

因为用优先级队列,我们需要知道结点的原始下标,所以还需创建结构体(包含权值和下标 )传入队列

struct IndexWeigth
{
    int index;//下标
    WeigthType weight;//权值
    operator WeigthType()const { return weight; }
};

创建

方法为上面的步骤

void CreatHuffManTree(HuffmanTree hft)
{
    priority_queue<IndexWeigth, vector<IndexWeigth>,greater<IndexWeigth>>qu;//greater比较权值
    for (int i = 1; i <= n; i++)
    {
        qu.push(IndexWeigth{ i,hft[i].weigth });//把叶子结点放到优先级队列内
    }
    int k = n + 1;//分支结点的下标
    while (!qu.empty())//取树构建新树
    {
        if (qu.empty())break;
        IndexWeigth left = qu.top(); qu.pop();//取第一小为左边
        if (qu.empty())break;
        IndexWeigth right = qu.top(); qu.pop();//取第二小为右边
//在分支结点上构建新树
        hft[k].weigth = left.weight + right.weight;
        hft[k].leftchild = left.index;
        hft[k].rightchild = right.index;
        hft[left.index].parent = k;
        hft[right.index].parent = k;
//将新树放入优先级队列
        qu.push(IndexWeigth{ k,hft[k].weigth });
        k++;
    }
}

输出

因为要建树,输出需要知道每个结点的权值、双亲和左右孩子

void PrintHuffManTree(HuffmanTree hft)
{
    for (int i = 1; i < m; i++)
    {
        printf("index%3d weight:%-3d parent:%-3d Lchild:%-3d Rchild:%-3d\n ",
              i,hft[i].weigth,hft[i].parent,hft[i].leftchild,hft[i].rightchild);
    }
    printf("\n");
}

主函数

int main()
{
    WeigthType w[n] = { 5,29,7,14,8,23,3,11 };
    HuffmanTree hft = { 0 };
    printf("初始带权树集合:\n");
    InitHuffManTree(hft, w);//初始化
    PrintHuffManTree(hft);//输出
    printf("哈夫曼树的结点关系:\n");
    CreatHuffManTree(hft);//创建
    PrintHuffManTree(hft);
    return 0;
}

最终结果画出的图:

A:11111 B :10 C :1110 D:110 E :000 F :01 G:11110 H:001


哈夫曼编码

发送电文时希望总长度尽可能的短(压缩),但是必须任意一个字符的编码不能是另一个字符的前缀,否则会有歧义。这种不是其他字符前缀编码的编码叫前缀码。

用哈夫曼树可以创建编码:其左分支表示字符‘0’,右分支表示字符‘1’,可以从根结点到叶子结点的路径上分支字符组成的字符串作为该叶子结点字符的编码。

叶子只有一个双亲所以创建编码时从叶子节点开始,最后把编码倒回来

编码结构体:

字符和其编码

typedef struct
{
    char ch;
    char code[n + 1];
}HuffCodeNode;
typedef HuffCodeNode HuffCoding[n + 1];//0下标不放数据

编码初始化

void InitHuffManCode(HuffCoding hc, const char* ch)
{
    memset(hc, 0, sizeof(HuffCoding));
    for (int i = 1; i <= n; i++)
    {
        hc[i].ch = ch[i - 1];
        hc[i].code[0] = '\0';
    }
}

创建编码

void CreatHuffManCode(HuffmanTree hft, HuffCoding hc)
{
    char code[n + 1] = { 0 };
    for (int i = 1; i <= n; i++)
    {//倒过来放
        int k = n;
        code[k] = '\0';
        int c = i;//孩子
        int pa = hft[c].parent;//找双亲
        while (pa != 0)//没有到根结点
        {
            code[--k] = hft[pa].leftchild==c ? '0' : '1';//是左孩子0,是右孩子1
            c = pa;
            pa = hft[c].parent;
        }
        //正着拷贝
        strcpy_s(hc[i].code, n, &code[k]);
    }
}

输出编码

void PrintfHuffManCode(HuffCoding hc)
{
    for (int i = 1; i <= n; i++)
    {
        printf("data:%c ->code:%s\n ", hc[i].ch, hc[i].code);
    }
}

主函数

int main()
{
    WeigthType w[n] = { 5,29,7,14,8,23,3,11 };
    char ch[n + 1] = "ABCDEFGH";
    HuffmanTree hft = { 0 };
    HuffCoding hc = { 0 };
    //初始带权树集合
    InitHuffManTree(hft, w);
    //编码初始化
    InitHuffManCode(hc, ch);
    //创建哈夫曼树
    CreatHuffManTree(hft);
    //创建编码
    CreatHuffManCode(hft, hc);
    //输出树结点关系
    PrintHuffManTree(hft);
    //输出编码
    PrintfHuffManCode(hc);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/187739.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mysql分组排序取组内第一的数据行获取分组后,组内排名第一或最后的数据行。

前言&#xff1a; group by函数后取到的是分组中的第一条数据&#xff0c;但是我们有时候需要取出各分组的最新一条&#xff0c;该怎么实现呢&#xff1f; 本文提供两种实现方式。 一、准备数据 DROP TABLE IF EXISTS tb_dept; CREATE TABLE tb_dept (id bigint(20) UNSIG…

chat聊天系统消息消费时遇到的问题及优化思路

前言 之前有段工作经历涉及到了chat相关&#xff0c;而消息的发送 -> 存储 -> 消费是由不同的团队负责的&#xff0c;因此消息如何再多个团队之间流通、以及通过什么介质传递都是需要考虑的问题。 之前我负责过一些消息消费的相关工作&#xff0c;消息发送团队将消息推…

【Linux】简介磁盘|inode|动静态库

目录一.简介磁盘1.磁盘的物理结构&#xff1a;2.磁盘存储方式&#xff1a;3.磁盘的逻辑抽象&#xff1a;二.inode&&文件系统1.inode文件属性&#xff08;inode&#xff09;内容&#xff08;data block&#xff09;为什么删除一个文件相比于写一个文件要快得多&#xff…

若依配置教程(二)集成积木报表JimuReport

积木报表配置官网 在搭建好若依环境成功运行以后&#xff0c;我们先在这个系统中加一个小功能&#xff1a;JimuReport积木报表&#xff0c;以下步骤&#xff0c;我们按照官网教程&#xff0c;详细配置一下&#xff1a; 1.在ruoyi-admin文件夹下的pom.xml加入jar包依赖&#x…

MLP多层感知机理解

目录 .1简介 .2例子 2.1模型 2.2 实例 2.2.1 问题描述 2.2.2 数学过程 .3 代码 3.1 问题描述 3.2 代码 references&#xff1a; .1简介 多层感知机是全连接的 可以把低维的向量映射到高维度 MLP整个模型就是这样子的&#xff0c;上面说的这个三层的MLP用公式总结起来…

C 语言零基础入门教程(二十)

C 预处理器 C 预处理器不是编译器的组成部分&#xff0c;但是它是编译过程中一个单独的步骤。简言之&#xff0c;C 预处理器只不过是一个文本替换工具而已&#xff0c;它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器&#xff08;C Preprocessor&#x…

练手好福利!20个Python实战项目含源代码【2023最新】

高效学习源代码的步骤&#xff1a;1.运行程序&#xff0c;观察表现2.运行源码&#xff0c;断点调试&#xff0c;从头跟一边源码的执行流程&#xff0c;注意函数堆栈3.画类图、流程图&#xff0c;先把遇到的重要类记录下来&#xff0c;表明各个类的关系4.记录问题&#xff0c;把…

Unity XR

一、几个Unity XR Interaction Toolkit学习地址 1.B站视频 https://www.bilibili.com/video/BV11q4y1b74z/?spm_id_from333.999.0.0&vd_source8125d294022d2e63a58dfd228a7fcf63 https://www.bilibili.com/video/BV13b4y177J4/?spm_id_from333.999.0.0&vd_source8…

【对象的比较】java代码实现,详解对象的比较,Comparable接口和Comparator比较器

前言&#xff1a; 大家好&#xff0c;我是良辰丫&#xff0c;&#x1f49e;&#x1f49e;&#x1f49e;今天的我们要学习的知识点是java对象的比较&#xff0c;不是大家现实生活中对象的比较&#xff0c;是java中new一个对象的那个对象&#xff0c;对象的比较到底是什么意思呢&…

24.网络编程(二)

目录 三.TCP通信 3.1 TCP协议特点 3.2 TCP协议通信场景 3.3 TCP通信模型演示 3.4 Socket 3.5 ServerSocket 3.6 注意事项 3.7 案例 3.7.1 TCP通信—单发单收 3.7.2 TCP通信—多发多收 3.7.3 TCP通信—同时接收多个客户端的消息。 3.7.4 TCP通信—使用线程池优化&am…

工业相机和镜头

工业相机和镜头镜头型号数据电源接口定焦镜头的调焦景深景深大小光圈相机、镜头选取参考镜头型号、数据电源接口、定焦镜头的调焦、景深、景深大小、光圈、相机、镜头选取 镜头型号 C&#xff0c;CS系列&#xff1a;相机镜头的C、CS接口非常相似&#xff0c;它们的接口直径、螺…

检索业务:基本数据渲染和排错

采用标签显示商品的数据 <div class"rig_tab"><div th:each"product:${result.getProducts()}"><div class"ico"><i class"iconfont icon-weiguanzhu"></i><a href"/static/search/#">…

5、数据的重构

目录 一、为什么进行数据重构 二、如何进行数据重构 一、为什么进行数据重构 进行数据分析时&#xff0c;有可能会发现数据的结构并不适合直接进行数据分析操作&#xff0c;如下面数据&#xff0c;但通过复制-粘贴-转置等方法操作又太繁琐&#xff0c;数据量小还行&#xff…

C++ 图进阶系列之 kruskal 和 Prim 算法_图向最小生成树的华丽转身

1. 前言 树和图形状相似&#xff0c;也有差异性。树中添加一条或多条边&#xff0c;可成图。图中减小一条或多条边&#xff0c;可成树。形态的变化由数据之间的逻辑关系决定。 图用来描述数据之间多对多关系。树用来描述数据之间一对多关系。 思考如下问题&#xff1f; 如果…

esp32 烧录协议

esp32的rom固化了出场固件。进入烧录模式后&#xff0c;esp32串口输出&#xff1a;给esp32烧录固件的时候&#xff0c;需要和rom的bootloder进行通讯。通讯时&#xff0c;使用 SLIP 数据包帧进行双向数据传输。每个 SLIP 数据包都以 0xC0 开始和结束。 在数据包中&#xff0c;所…

9、Servlet——Request对象

目录 一、get请求和post请求的区别 二、Request对象的应用 1、request主要方法 2、request获取数据 3、设置请求的编码格式 三、解决get请求收参乱码问题 四、解决post请求中文乱码问题 一、get请求和post请求的区别 在Servlet中用来处理客户端请求需要用doGet()方法或…

openGauss数据库源码解析系列文章——备份恢复机制:openGauss全量备份技术

目录 10.1 openGauss全量备份技术 10.1.1 gs_basebackup备份工具 10.1.2 gs_basebackup备份交互流程 本文主要介绍openGauss的备份恢复原理和技术。备份恢复是数据库日常维护的一个例行活动&#xff0c;通过把数据库数据备份到另外一个地方&#xff0c;可以抵御介质类的损…

数据结构与算法-稀疏数组

Java高级系列文章前言 本文章涉及到数据结构与算法的知识&#xff0c;该知识属于Java高级阶段&#xff0c;通常为学习的二阶段&#xff0c;本系列文章涉及到的内容如下&#xff08;橙色框选内容&#xff09;&#xff1a; 本文章核心是教学视频&#xff0c;所以属于个人笔记&a…

深度卷积对抗神经网络 基础 第六部分 缺点和偏见 GANs Disadvantages and Bias

深度卷积对抗神经网络 基础 第六部分 缺点和偏见 GANs Disadvantages and Bias GANs 综合评估 生成对抗网络&#xff08;英语&#xff1a;Generative Adversarial Network&#xff0c;简称GAN&#xff09;是非监督式学习的一种方法&#xff0c;透过两个神经网络相互博弈的方式…

实体对齐(三):RNM

一.摘要 实体对齐旨在将来自不同知识图&#xff08;KG&#xff09;的具有相同含义的实体联系起来&#xff0c;这是知识融合的重要步骤。 现有研究侧重于通过利用知识图谱的结构信息学习实体嵌入来进行实体对齐。这些方法可以聚合来自相邻节点的信息&#xff0c;但也可能带来来…