【c++哈夫曼树代码实现】

news2024/12/26 22:21:13

哈夫曼树是不定长编码方式,由于是将权值大的元素放在离根结点近的地方 ,权值小的放在离根远的地方,哈夫曼树效率很高,并且一个编码不会以另一个编码作为前缀,避免了编码的歧义性,本文将带大家探索如何创建和使用哈夫曼树。

在这里插入图片描述

构造哈夫曼树

n个元素构造哈夫曼树需要2n-1个结点,为什么是这个结果呢?接下来我们看一下哈夫曼树的构造原理

  1. 从所有(未确定双亲结点)元素中选出两个权值最小的元素
  2. 选出来的元素的权值的和为新的元素的权值(新的元素将是这两个元素的双亲结点),再将新的元素放入原来的元素集合中(已经确定双亲结点则不用放回集合中),现在集合中元素数量-1,但总元素个数+1,因为新生成了一个元素。
  3. 重复 1,2过程,直到元素集合中只有1个元素。

很容易看出总共要重复n-1次1,2操作(因为每次操作后集合元素数量要-1,当集合元素的数量为2时就是最后一次操作,此时总共进行了n-1次操作)每进行一次操作都会生成一个新元素,所以总共生成了n-1个新元素,再加上原来的元素,总共2*n-1个。
这里我们用结构体数组维护哈夫曼树,清楚原理后我们来看下具体代码:
在这里插入图片描述

#include <iostream>
using namespace std;
//最大元素个数
const int N=1e5;

typedef struct{
    int parent;
    int lchild,rchild;
    int weight;
}HuffNode,*HUffTree;

//第0个位置不使用,下标从1开始
HuffNode tree[N*2];
void HuffMan(HuffNode tree[],int w[],int n){
    //p1最小的权值的结点索引,p2第二小的结点的索引
    int i,j,p1,p2;
    int small1,small2;
    //要求最小值,所以初始化要赋很大的值
    small1=small2=0x3fff;

    //初始化每个结点
    for(i=1;i<=n;i++){
        tree[i].weight=w[i];
        tree[i].parent=0;
        tree[i].lchild=0;
        tree[i].rchild=0;
    }
    //初始化其他结点
    for(;i<2*n;i++){
        tree[i].parent=0;
        tree[i].lchild=0;
        tree[i].rchild=0;
    }
    for(i=n+1;i<2*n;i++){
        for(j=1;j<i;j++){
            //如果结点的双亲结点为0,说明改结点还在元素集合中
            if(tree[i].parent==0){
                if(tree[i].weight<small1){
                    //已经找到一个比原来最小的元素还小的元素,那原来最小的现在成了第二小
                    small2=small1;
                    p2=p1;
                    //更新最小元素
                    small1=tree[i].weight;
                    p1=j;
                }
                else if(tree[i].weight<small2){
                    small2=tree[i].weight;
                    p2=j;
                }
            }
        }
        //找到权值最小的两个结点后,要生成新的结点
        tree[i].weight=small1+small2;
        tree[i].lchild=p1;
        tree[i].rchild=p2;
        //更新最小的两个结点的双亲为新结点
        tree[p1].parent=tree[p2].parent=i;
    }
    
}

哈夫曼树编码

在已经建好的哈夫曼树上,从叶子结点开始沿着双亲结点找根,每退回一步就到了一个分支,从而得到一位哈夫曼编码的值。因为每一个字符的哈夫曼编码都是根结点到叶子结点的路径分支上的0、1序列,所以先得到的编码是低位码,后得到的是高位码,可以设置二维字符数组来保存每个哈夫曼编码。

char HuffManCode[N][N];
void huffmancodding(HuffNode tree[],int n){
    //存储每次循环后字符的哈夫曼编码
    char * tem = (char*)malloc(n*(sizeof(char)));
    int  parent;

    //结束标志
    tem[n-1] = '\0';

    //叶子结点是前n个结点
    for(int i =1;i<=n;i++){
           int idx=n-1;
        for(int j =i;tree[j].parent!=0;j=tree[j].parent){
            //得到双亲结点
            parent = tree[j].parent;
            //左分支为 0,右分支为 1
            if(tree[parent].lchild==j){
                tem[--idx]='0';
            }
            else{
                tem[--idx]='1';
            }

        }
        //在C语言中,数组是一个连续存储的数据结构,其中每个元素都有一个固定的地址。数组名实际上是指向数组第一个元素的指针。
        //对于二维数组b[n][m],它实际上是按行优先或列优先的方式在内存中进行存储的一维数组。
        // 因此,b[i]实际上是代表了第i+1行(如果从0开始计数)的首地址,也就是指向这一行第一个元素的指针。
        strcpy(HuffManCode[i],&tem[idx]);

    }
    free(tem);

    for(int i =1;i<=n;i++)
        printf("%d:   %s\n",i,HuffManCode[i]);
}

测试

按照这个图来检验一下我们的代码
请添加图片描述
请添加图片描述

#include <iostream>
#include <cstring>
using namespace std;
//最大元素个数
const int N=100;

typedef struct{
    int parent;
    int lchild,rchild;
    int weight;
}HuffNode;

//第0个位置不使用,下标从1开始
HuffNode tree[N*2];
void HuffMan(HuffNode tree[],int w[],int n){
    //p1最小的权值的结点索引,p2第二小的结点的索引
    int i,j,p1,p2;
    int small1,small2;
    //要求最小值,所以初始化要赋很大的值
    small1=small2=0x3fff;

    //初始化每个结点
    for(i=1;i<=n;i++){
        tree[i].weight=w[i];
        tree[i].parent=0;
        tree[i].lchild=0;
        tree[i].rchild=0;
    }
    //初始化其他结点
    for(;i<2*n;i++){
        tree[i].parent=0;
        tree[i].lchild=0;
        tree[i].rchild=0;
    }
    for(i=n+1;i<2*n;i++){
        small1=small2=0x3fff;
        for(j=1;j<i;j++){
            //如果结点的双亲结点为0,说明改结点还在元素集合中
            if(tree[j].parent==0){
                if(tree[j].weight<small1){
                    //已经找到一个比原来最小的元素还小的元素,那原来最小的现在成了第二小
                    small2=small1;
                    p2=p1;
                    //更新最小元素
                    small1=tree[j].weight;
                    p1=j;
                }
                else if(tree[j].weight<small2){
                    small2=tree[j].weight;
                    p2=j;
                }
            }
        }
        //找到权值最小的两个结点后,要生成新的结点
        tree[i].weight=small1+small2;
        tree[i].lchild=p1;
        tree[i].rchild=p2;
        //更新最小的两个结点的双亲为新结点
        tree[p1].parent=tree[p2].parent=i;
    }

}
char HuffManCode[N][N];
void huffmancodding(HuffNode tree[],int n){
    //存储每次循环后字符的哈夫曼编码
    char * tem = (char*)malloc(n*(sizeof(char)));
    int  parent;

    //结束标志
    tem[n-1] = '\0';

    //叶子结点是前n个结点
    for(int i =1;i<=n;i++){
           int idx=n-1;
        for(int j =i;tree[j].parent!=0;j=tree[j].parent){
            //得到双亲结点
            parent = tree[j].parent;
            //左分支为 0,右分支为 1
            if(tree[parent].lchild==j){
                tem[--idx]='0';
            }
            else{
                tem[--idx]='1';
            }

        }
        //在C语言中,数组是一个连续存储的数据结构,其中每个元素都有一个固定的地址。数组名实际上是指向数组第一个元素的指针。
        //对于二维数组b[n][m],它实际上是按行优先或列优先的方式在内存中进行存储的一维数组。
        // 因此,b[i]实际上是代表了第i+1行(如果从0开始计数)的首地址,也就是指向这一行第一个元素的指针。
        strcpy(HuffManCode[i],&tem[idx]);

    }
    free(tem);

    for(int i =1;i<=n;i++)
        printf("%d:   %s\n",i,HuffManCode[i]);
}
int main(){
    int w[7]={0,6,2,3,9,10,8};
    HuffMan(tree,w,6);
   huffmancodding(tree,6);

}

请添加图片描述

总结

本文介绍了哈夫曼树的创建和如何编码,希望本文能帮助到正在学习哈夫曼树的读者。在这里插入图片描述

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

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

相关文章

SAP smartform 实现打印条形码

先在SE73里定义一个新的BARCODE&#xff0c;注意一定要用新的才可以&#xff0c;旧的是打印不出来的。 然后定义一个SMARTFORM的样式&#xff0c;把你定义的BARCODE放到字符样式里面去。 再做SMARTFORM就可以了&#xff0c;将需要作为条码的变量的格式选为该BARCODE格式&…

ASP.NET Core 使用 SignalR 实现实时通讯

&#x1f433;简介 SignalR是一个用于ASP.NET的库&#xff0c;它允许服务器代码向连接的客户端实时发送推送通知。它使用WebSockets作为底层传输机制&#xff0c;但如果浏览器不支持WebSockets&#xff0c;它会自动回退到其他兼容的技术&#xff0c;如服务器发送事件&#xff…

GEE:众数滤波

作者:CSDN @ _养乐多_ 在本文中,我们将介绍如何使用Google Earth Engine(GEE)平台对遥感影像进行众数滤波处理。并以众数滤波平滑NDVI图像为示例,演示众数滤波整个过程。 结果如下图所示, 文章目录 一、众数滤波二、完整代码三、代码链接一、众数滤波 众数滤波是一种图…

保护您的IP地址:预防IP地址盗用的关键措施

随着互联网的发展&#xff0c;IP地址作为标识互联网设备的重要元素&#xff0c;成为网络通信的基石。然而&#xff0c;IP地址盗用威胁正不断增加&#xff0c;可能导致敏感信息泄露、未经授权的访问和网络攻击。本文将介绍一些有效的方法&#xff0c;以帮助组织和个人预防IP地址…

【外贸干货】领英客户开发与营销的六个策略方向

领英(LinkedIn)已经成为外贸营销人员&#xff0c;尤其是B2B外贸营销人员&#xff0c;一个重要且有效的社交媒体平台。 相比于其他社交媒体平台&#xff0c;领英(LinkedIn)在增加流量、产生高质量的潜在客户和建立思想领导力方面有着独有的优势。 因为领英(LinkedIn)不仅仅是获…

【力扣:1707 1803】0-1字典树

思路&#xff1a;树上每个节点存储拥有该节点的数组元素的最小值&#xff0c;left节点表示0&#xff0c;right节点表示1&#xff0c;构建完成后遍历树当子节点没有比mi小的元素时直接输出-1&#xff0c;否则向下构造。 struct tree{int m;tree*leftnullptr,*rightnullptr;tree…

flink源码分析之功能组件(二)-kubeclient

简介 本系列是flink源码分析的第二个系列,上一个《flink源码分析之集群与资源》分析集群与资源,本系列分析功能组件,kubeclient,rpc,心跳,高可用,slotpool,rest,metrics,future。其中kubeclient上一个系列介绍过,为了系列完整性,这里“copy”一下。 kubeclient组件…

ffmpeg下载与配置环境变量

FFmpeg 是一个强大的多媒体框架&#xff0c;可以让用户处理和操纵音频和视频文件。具有易于使用的界面&#xff0c;用户可以在 Windows、Mac 或 Linux Ubuntu 系统上下载 FFmpeg 并将其提取到文件夹中。然后&#xff0c;该软件可以加入 PATH 环境变量中就可以快捷的使用软件了.…

中职组网络安全-Windows操作系统渗透测试 -20221219win(环境+解析)

B-4:Windows操作系统渗透测试 任务环境说明: 服务器场景:20221219win 服务器场景操作系统:Windows(版本不详)(封闭靶机) 1.通过本地PC中渗透测试平台Kali对服务器场景Server08进行系统服务及版本扫描渗透测试,并将该操作显示结果中1433端口对应的服务版本信息作为F…

深度学习之图像分类(十四)CAT: Cross Attention in Vision Transformer详解

IPSA和CPSA的处理流程、维度变换细节 FLOPs的计算方法、以及flops和划分的patch数目以及patch的维度计算关系 IPSA如何进行local attention、CPSA如何进行globe attention CAT的代码详细注释---需要学习完Transformer TNT、swin transformer、crossViT CAT: Cross Atten…

软件测试 | MySQL 非空约束详解

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

JavaWeb学习(未完结)

文章目录 一、基本概念1.1 动态Web网站简介1.2 web应用程序1.3 静态web1.4 动态web 二、web服务器2.1 技术2.2 应用服务器2.3 安装 jdk8 三、Tomcat3.1 安装 Tomcat93.2 文件说明3.3 启动并使用Tomcat3.4 关闭Tomcat3.5 可能遇到的问题3.6 配置3.6.1 修改测试访问的网页地址3.6…

LLM之Agent(一):使用GPT-4开启AutoGPT Agent自动化任务完整指南

在ChatGPT引领的大模型时代&#xff0c;要想让大模型按照用户的指令执行&#xff0c;Prompt设计是一门艺术&#xff0c;由此还催生了一个职业”Prompt工程师“。其实&#xff0c;并不是所有人都可以设计出好的Prompt&#xff0c;甚至同样的Prompt应用在不同的大模型上表现的结果…

Mysql数据库 20.DCL数据控制语言

因这类SQL语言开发人员操作较少&#xff0c;主要是数据库管理员&#xff08;DBA&#xff09;使用&#xff0c;所以前文没有提及&#xff0c;这篇文章进行补充说明 DCL数据控制语言 用来管理数据库用户&#xff0c;控制数据库的访问权限 1.管理用户 1.1 查询用户 select * f…

想当老师应该去学什么专业

专业选择是决定未来职业发展的重要步骤&#xff0c;如果你也想成为一名老师&#xff0c;那么这五个专业可能会适合你&#xff01; 教育学专业 教育学专业是培养教育理论和方法的学科&#xff0c;这些理论知识将帮助你理解教学过程、学生发展、课程设计和评估。该专业将让你全面…

从入门到精通:JMeter接口测试全流程详解!

利用Jmeter做接口测试怎么做呢&#xff1f;过程真的是超级简单。 明白了原理以后&#xff0c;把零碎的知识点填充进去就可以了。所以在学习的过程中&#xff0c;不管学什么&#xff0c;我一直都强调的是要循序渐进&#xff0c;和明白原理和逻辑。这篇文章就来介绍一下如何利用…

互联网时代的身份标识有哪些?

在互联网时代&#xff0c;我们的在线活动几乎都与IP地址相关。无论是浏览网页、观看视频&#xff0c;还是进行在线交易和沟通交流&#xff0c;我们的设备都会分配到一个独特的IP地址。然而&#xff0c;你可能并未意识到的是&#xff0c;IP地址不仅标识了我们在网络中的身份&…

Java零基础——Linux篇

1.【熟悉】认识Linux 1.1 什么是操作系统 1.2 现实生活中的操作系统 1.2.1 Win10 1.2.2 Mac 1.2.3 Android(Linux) 1.2.4 iOS(Unix) 1.3 操作系统的发展史 1.3.1 Unix 1965年之前的时候&#xff0c;电脑并不像现在一样普遍&#xff0c;它可不是一般人能碰的起的&#xff0c…

怎样提升伦敦银买卖技巧?

如果投资者想提升伦敦银的买卖技巧&#xff0c;可以学习一些有用的技术分析方法。所谓技术分析&#xff0c;就是通过对行情过往价格和相关交易数据进行收集&#xff0c;用图表的方式解读白银市场&#xff0c;进而预测行情未来主线走势、判断价格细节变化、寻找重要支撑点阻力点…

外贸分享|如何从外贸小白成长为大咖?这10件事值得你坚持做

外贸成功不是一朝一夕的事&#xff0c;而是需要有充分的准备和持续的努力。作为一位有着丰富经验的外贸人员&#xff0c;我总结了成功的秘诀&#xff0c;分享了一个优秀的外贸人应该做好的10项工作。 1 找不到客户怎么办&#xff1f; 有很多各种各样的原因值得思考&#xff1a…