哈夫曼树及哈夫曼编码

news2025/1/12 7:44:42

目录

一. 前言

二. 哈夫曼树的构造

三. 哈夫曼编码


一. 前言

        在学习哈夫曼树之前,我们先了解几个基本概念。

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

2.结点的路径长度:两结点间路径上的分支数。

3.树的路径长度:从树根到每一个结点的路径长度之和。记作:TL。

4.通过分析可以知道,结点数相同的二叉树中,完全二叉树是路径长度最短的二叉树。但路径长度最短的二叉树不一定是完全二叉树。

5.权:将树中结点赋给一个有着某种含义的数值,这个数值称为该结点的权。

6.结点的带权路径长度:从根结点到该结点之间的路径长度与该结点的权的乘积。

7.树的带权路径长度:树中所有叶子结点的带权路径长度

在有了上面的知识作基础之后,我们就能理解哈夫曼树的定义了。

所谓哈夫曼树就是指带权路径长度最短的树,也叫最优树。当它为最优二叉树的时候就是指的带权路径长度(WPL)最短的二叉树。

并且我们可以知道它的特点,就是满二叉树不一定是最优二叉树。最优二叉树中权越大的数离根越近。并且具有相同带权结点的哈夫曼树不唯一。

二. 哈夫曼树的构造

        实现哈夫曼树的算法步骤可以分为以下几步:

1)首先根据n个给定的权值来构造n棵二叉树的森林F。森林F中的每一棵树都只有一个结点,也就是根结点。可以使用这样一个口诀来加以记忆这一步骤:构造森林全是根。

2)接着在这个森林F中选取两棵根结点的权值最小的树作为左右子树,用来构造一棵新的二叉树。并且设置新的二叉树的根结点的权值为其左右子树上根结点的权值之和。记忆口诀为:选用两小造新树。

3)然后在这个森林F中删除这两棵树,同时将新得到的二叉树加入到这个森林F中。记忆口诀为:删除两小添新人。

4)重复2)3)两个步骤,直到森林中只有一棵树为止,这棵树就是哈夫曼树。

我们可以看下面一个例子来巩固掌握哈夫曼树的实现:

 首先就是构造森林全是根。然后找到权值最小的两棵树,也就是d和e,它们的权值最小为2和4。将它们构成一个新的二叉树加入到森林中,然后重复这两步的操作。得到的最后只有一棵树,也就是我们所求的哈夫曼树。

并且通过分析,我们可以发现,假设初始时有n棵二叉树,其中的二叉树两两进行合并,那么就需要经过n-1次合并最终才形成哈夫曼树,经过n-1次合并产生n-1个新结点,且这n-1个新结点都是具有两个孩子的分支结点。

由此可见,哈夫曼树中一共有n+n-1=2n-1个结点,且其所有的分支结点的度均不为1。

在知道了哈夫曼树的原理和实现之和,我们该怎么用代码表示出来呢?

下面我将给出哈夫曼构造算法的完整代码,包括找两个最小权值的根结点(也就是还没合并的两个二叉树,还没有双亲)的函数,以及打印哈夫曼树中序号为1的结点数据。同学们也可自行在我的代码基础上添加一些其他功能。

//构造哈夫曼树的结点类型
typedef struct HTNode{
    int weight;    //权值
    int parent,lchild,rchild;    //结点的左右孩子以及双亲的位置序号
}HTNode,*HuffmanTree;

//函数的声明
void Choice(HuffmanTree HT,int n,int&s1,int&s2);

//最后结果会返回一个哈夫曼树的函数
HuffmanTree CreatHuffmanTree(int n){    
    if(n<=1) exit;   //如果结点数小于2,会退出
    m=2*n-1;    //带权的结点数为n,那么整个哈夫曼树就会有2*n-1个结点。
    HuffmanTree HT=new HTNode[m+1];  //一般不使用序号为0的位置,所以m再加1
    for(int i=1;i<=m;i++){        //将里面所有的元素的这些属性先赋值为0
        HT[i].lchild=0;
        HT[i].rchild=0;
        HT[i].parent=0;
    }
    for(int i=1;i<=n;i++){    //输入前n个树的权值
        scanf("%d",&HT[i].weight);
    }
    for(i=n+1;i<=m;i++){    //开始进行合并,总共合并n-1次
        Choice(HT,i-1,s1,s2);   //这个choice函数就是在HT[k](1<=k<=i-1)中选择两个双亲为0,也就是没有被合并的根结点,并且权值最小的结点,返回它们在HT中的序号s1和s2.
        HT[s1].parent=i;    //其中序号为s1的根结点有了双亲
        HT[s2].parent=i;    //跟s1是兄弟关系
        HT[i].lchild=s1;    //给这个n+1的位置上的树分配左右孩子
        HT[i].rchild=s2;
        HT[i].weight=HT[s1].weight+HT[s2].weight;   //权值为这两个子树之和
    }
    return HT;  //在完成了上面的哈夫曼树构造之后,返回一个哈夫曼树
}


void Choice(HuffmanTree HT,int n,int&s1,int&s2){
    int min1=10000;
    int min2=10000;
    s1=s2=0;    //先给这两个变量初始化
    for(int i=1;i<=n;i++){
        if(HT[i].parent==0&&HT[i].weight<min1){   
     //用min1来存放其中权值最小的,min2来存放其中权值第二小的。所以一旦有比min1还要小的数,就先            
     //将min1现在的值赋值给min2,接着再把这个更小的数赋值给min1.
            min2=min1;
            s2=s1;
            min1=HT[i].weight;
            s1=i;
        }
        else if(HT[i].parent==0&&HT[i].weight<min2){    //只比第二小的数小,则只修改min2的值
            min2=HT[i].weight;
            s2=i;
        }
    }
}

int main(){
      int n=4;    //带权的结点数为4
      HuffmanTree HT=CreatHuffmanTree(n);    //用HT来接收在CreatHuffmanTree函数中已经构造好了的哈夫曼树
      printf("%d",HT[1].weight);    //简单测试下,打印一号位置的权值
}


三. 哈夫曼编码

        哈夫曼编码就是将要发送的数据(可能是数字也有可能是字符)转换成由二进制组成的字符串。举个简单例子:例如发送ABC,将A的编码设置成1,B的编码设置成2,C的编码设置成3。那么最后发送的就是123。哈夫曼编码要求传送的字符串中出现次数较多的字符采用尽可能短的编码,那么转换成的二进制字符串便可能减少,并且是前缀编码。例如1是2的前缀编码,而0不是01的前缀编码,会造成重码的问题。

实现哈夫曼编码的方法:

在知道了哈夫曼编码的原理和方法之后,我们如何用代码实现呢?

如下所示:

//从叶子到根逆向求每一个字符的哈夫曼编码,并存储到编码表HC中
void CreatHuffmanCode(HuffmanTree HT,HuffmanCode&HC,int n){
    HC=new char*[n+1];    //分配n个字符编码的头指针矢量
    cd=new char[n];       //分配临时存放编码的动态数组空间
    cd[n-1]='\0';        //最后一个位置存放结束符,编码最长为n-1,因此空间不会小
    for(int i=1;i<=n;i++){    //逐个字符求哈夫曼编码
        int start=n-1;
        int c=i;
        int f=HT[i].parent;
        while(f!=0){        //从叶子结点开始往上回溯,直到根结点
            --start;
            if(HT[f].lchild==c) cd[start]='0';   //结点c是f的左孩子,则生成代码0
            else cd[start]='1';        //右孩子生成代码1
            c=f;                //继续向上回溯
            f=HT[f].parent;
        }
        HC[i]=new char[n-start];        //为第i个字符的编码分配空间
        strcpy(HC[i],&cd[start]);       //将所求得编码从临时空间cd中赋值到HC当前位置上
     }
    delete cd;    //释放临时空间
}

 

 

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

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

相关文章

Visual Studio 2022 安装步骤

一、官方下载 下载地址&#xff1a;https://visualstudio.microsoft.com/zh-hans/vs/ 下载完成&#xff0c;得到VisualStudioSetup.exe文件 二、安装 Visual Studio 2022 由于我有 Professional 版本的密钥&#xff0c;因此安装Professional 版本。 Visual Studio 2022 Profes…

libtorch学习历程(一):libtorch2.4.0+opencv4.10+Qt6安装

开发环境 Win11RTX4060CUDA12.1libtorch 2.4.0 ,CUDA12.1,release版VS2022opencv4.10版本Qt 6.6.3 软件下载 libtorch下载 直接去pytorch官网下载即可&#xff0c;根据需要下载realease与debug版。 而CUDA的版本则查看CUDA版本教程&#xff0c;来选择合适的。 debug版与r…

大数据核心概念与技术架构简介

大数据基本概念 大数据是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合&#xff0c;是具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。 大数据特征&#xff1a; 数据量大&#xff1a;一般以P&#xff08;1000个TB&a…

详解DDR3原理以及使用Xilinx MIG IP核(app 接口)实现DDR3读写测试

系列文章目录 &#xff08;1&#xff09;详解SDRAM基本原理以及FPGA实现读写控制 文章目录 系列文章目录一、DDR简介1.1 什么是 SDRAM、DDR、DDR2、DDR31.2 SDRAM、DDR、DDR2、DDR3核心频率、工作频率以及等效频率的计算1.3 DDR3带宽以及容量的计算 二、MIG IP核的介绍三、MIG…

Datawhale AI夏令营第三期 AI+物质科学学习记录 Task2 RNN建模SMILES进行反应产率预测

引导 如何将化学知识转化为计算机可识别的形式 提到化学应该会想到元素周期表&#xff0c;分子式&#xff0c;化学反应方程等等&#xff0c;化学这门学科的知识和内容都有专有符号记录的&#xff0c;但计算机不能理解&#xff0c;所以需要将这些符号让计算机能理解 Ai4Chemi…

python orm框架有哪些

ORM概念 ORM&#xff08;Object Ralational Mapping&#xff0c;对象关系映射&#xff09;用来把对象模型表示的对象映射到基于SQL的关系模型数据库结构中去。这样&#xff0c;我们在具体的操作实体对象的时候&#xff0c;就不需要再去和复杂的SQL语句打交道&#xff0c;只需简…

5 postgresql 并行查询

并行扫描会占用更多的cpu/IO内存资源&#xff0c;开启的时候要小心配置。 大部分场景都能提高查询效率 之前有过一个生产案例&#xff0c;开启并行之后性能反而没有普通的好&#xff0c;原因就是占有了太多资源&#xff0c;查询慢了&#xff0c;增加表锁概率&#xff0c;产生…

ISO 14001:引领企业绿色革命的环境管理体系

ISO 14001 以其预防为主、系统化管理的特点&#xff0c;在众多管理体系中脱颖而出。该标准强调通过识别和控制环境因素&#xff0c;提前预防潜在环境问题&#xff0c;从根本上减少企业对环境的负面影响。这种预防性管理模式&#xff0c;不仅能够有效保护环境&#xff0c;还能降…

.[datastore@cyberfear.com].mkp勒索病毒数据怎么处理|数据解密恢复

引言 近年来&#xff0c;网络安全威胁日益严峻&#xff0c;其中勒索病毒成为了一种极具破坏性的恶意软件。特别是[conkichinmodlconkichinmodl.com].mkp 、[datastorecyberfear.com].mkp勒索病毒&#xff0c;其通过加密用户文件并索取赎金的方式&#xff0c;给受害者带来了巨大…

15.列表框文本域和文件域

一、列表框 <!-- 下拉框&#xff0c;列表框 --><p>城市&#xff1a;<select name"城市"><option value"Guangzhou">广州</option><option value"Shenzhen" selected>深圳</option><option value&…

Zabbix 7.0 安装

在zabbix官网中有着比较完善的安装步骤&#xff0c;针对不同的系统都有。可以直接按照举例说明进行安装。本文只是针对其提供的安装步骤进行一些说明解释补充。 安装环境 操作系统版本&#xff1a;AlmaLinux 9.4&#xff08;10.10.20.200&#xff09;zabbix版本&#xff1a;7.…

A股震荡前行,3000点指日可待?

今天的A股&#xff0c;让人瞬间呆住了&#xff0c;你知道是为什么吗&#xff1f;盘面上出现2个耐人寻味的重要信号&#xff0c;一起来看看&#xff1a; 1、今天A股小幅洗盘&#xff0c;涨了一天就熄火了&#xff1f;让人瞬间呆住了。不慌&#xff01;敢于回踩&#xff0c;才能更…

两个好消息,你先听哪个?

1.第五大数据、人工智能与软件工程国际研讨会&#xff08;ICBASE 2024)成功申请IEEE出版&#xff0c;上线IEEE官网&#xff0c;欢迎投稿参会&#xff01;&#xff01;&#xff01; &#x1f4e3;IEEE独立出版&#xff0c;设置优秀评选 &#x1f525;院士加盟&#xff0c;中外高…

C嘎嘎浅聊string

那么这篇文章我们开始学习 c嘎嘎的string stl简介 什么是stl STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的 组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架 stl版本 原始版本、P.J.版本、RW版…

机械学习—零基础学习日志(高数18——无穷小与无穷小)

零基础为了学人工智能&#xff0c;真的开始复习高数 学习速度加快&#xff01; 无穷小定义 这里可以记住&#xff0c;无穷小有一个特殊&#xff0c;那就是零。 零是最高阶的无穷小&#xff0c;且零是唯一一个常数无穷小。 张宇老师还是使用了超实数概念来讲解无穷小。其实是…

在云服务器上自动化部署项目,jenkins和gitee

▮全文概述 在编写项目时&#xff0c;很头大的事情就是需要自己手动的上传jar包到服务器上启动。如果出现一点bug&#xff0c;就要重头上传和启动。这是一件很烦的事情&#xff0c;所以&#xff0c;可以使用jenkins和gitee实现项目的自动部署 ▮全流程 在本地提交代码到gitee …

kvm+glusterfs+pcs分布式存储构建kvm高可用群集。

kvmglusterfspcs分布式存储构建kvm高可用群集。 高可用指能在短时间内自动完成故障转移&#xff0c;kvm在实现故障自动转移的方式如果要考虑速度那么基于共享存储的迁移方法无疑是最快的。而共享存储的高可用就可以用glusterfs来实现。 glusterfs可以利用分布式复制卷兼顾了性能…

图解RocketMQ之消费者如何进行消息重试

大家好&#xff0c;我是苍何。 在上一篇[[图解RocketMQ之生产者如何进行消息重试]]中分析了当生产者发送消息失败的时候&#xff0c;RocketMQ 是如何进行重试的。 最后留了一个问题&#xff0c;生产端的消息是重试 hold 住了&#xff0c;但如果消费者出现异常&#xff0c;消费…

leetcode53. 最大子数组和(java实现)

今天分享的题目是leetcode53题 本题的解题思路&#xff1a;贪心算法。 举个例子&#xff1a; nums [-2,1,-3,4,-1,2,1,-5,4]&#xff0c;如果说我们的累加和是负数那么就将其舍弃&#xff0c;换下一个位置从新进行累加&#xff0c;因为如果目前我们的累加和如果是负数那么还不…

Zotero的探索之路

下载安装 Zotero | Downloads&#xff1a;点击链接&#xff0c;不仅要下载软件本身&#xff0c;也要下载浏览器connector&#xff0c;这样就方便从网页抓取论文到zotero本地了&#xff0c;浏览器是使用插件导入下载包即可 导入文献方法 zotero自动下载&#xff08;推荐&#…