数据结构—哈夫曼树及其应用

news2024/11/15 16:00:35

5.6哈夫曼树及其应用

5.6.1哈夫曼树的基本概念

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

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

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

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

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

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

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

哈夫曼树 - 知乎

哈夫曼树最优树 带权路径长度(WPL)最短的树

注意:“带权路径长度最短”是在“度相同”的树中比较而得的结果,因此有最优二叉树、最优三叉树之称等等。

哈夫曼树最优二叉树 带权路径长度(WPL)最短的二叉树

因为构造这种树的算法是由哈夫曼教授于1952年提出的,所以被称为哈夫曼树,相应的算法称为哈夫曼算法。

哈夫曼树的特点:

满二叉树不一定是哈夫曼树

哈夫曼树中权越大的叶子离根越近

具有相同带权结点的哈夫曼树不唯一

5.6.2哈夫曼树的构造算法

数据结构与算法 - 哈夫曼树 - 极客分享

哈夫曼树中权越大的叶子离根越近

贪心算法:构造哈夫曼树时首先选择权值小的。

哈夫曼算法(构造哈夫曼树的方法)

  1. 根据 n 个给定的权值{W1,W2,…,Wn}构成 n 棵二叉树的森林F={T1,T2,…,Tn},其中Ti只有一个带权为Wi的根结点。
    • 构造森林全是根
  2. 在F中选取两棵根结点的权值最小的树作为左右子树,构造一棵新的二叉树,且设置新的二叉树的根结点的权值为其左右子树上根结点的权值之和。
    • 选用两小造新树
  3. 在F中删除这两棵树,同时将新得到的二叉树加入森林中。
    • 删除两小添新人
  4. 重复(2)和(3),直到森林中只有一棵树为止,这棵树即为哈夫曼树。
    • 重复2、3剩单根

哈夫曼树 深入剖析 - 知乎

哈夫曼树的结点的度数为0或2,没有度为1的结点。

包含 n 个叶子结点的哈夫曼树中共有 2n-1 个结点。

包含 n 棵树的森林要经过 n-1 次合并才能形成哈夫曼树,共产生 n-1 个新结点。

img

总结:

  1. 在哈夫曼算法中,初始时有 n 棵二叉树,要经过 n-1 次合并最终形成哈夫曼树。
  2. 经过 n-1 次合并产生 n-1 个新结点,且这 n-1 个新结点都是具有两个孩子的分支结点。
  3. 哈夫曼树中共有 n+n-1=2n-1 个结点,且其所有的分支结点的度均不为1。

5.6.3哈夫曼树构造算法的实现

采用顺序存储结构——一维结构数组

结点类型定义:

typedef struct{
  int weight;
  int parent,lch,rch;
}HTNode,*HuffmanTree;

哈夫曼树的构造(二),哈夫曼树,构造

哈夫曼树及哈夫曼编码_fireflylane的博客-CSDN博客_不等长哈夫曼编码是什么意思

  1. 初始化HT[1…2n-1]:lch = rch = parent = 0;

  2. 输入初始 n 个叶子结点:置HT[1…n]的weight值;

  3. 进行一下n-1次合并,依次产生n-1个结点HT[i],i=n+1…2n-1:

    a)在HT[1…i-1]中选两个未被选中(从parent==0的结点中选)的weight最小的两个结点HT[s1]和HT[s2],s1,s2为两个最小结点下标;

    b)修改HT[s1]和HT[s2]的parent值:HT[s1].parent=i;HT[s2].parent=i;

    c)修改新产生的HT[i]:

    • HT[i].weight=HT[s1].weight + HT[s2].weight;
    • HT[i].lch=s1;HT[i].rch=s2
void CreatHuffmanTree (HuffmanTree HT,int n){
  if(n<=1)return;
  m=2*n-1;//数组共有2n-1个元素
  HT=new HTNode[m+1];//0号单元未用,HT[m]表示根结点
  for(i=0;i<=m;++i){//将2n-1个元素的lch,rch,parent置为0
    HT[i].lch=0;
    HT[i].rch=0;
    HT[i].parent=0;
  }
  for(i=1;i<=n;++i)//输入前n个元素的weight
    cin>>HT[i].weight;
  for(i=n+1;i<=m;i++){
    Select(HT,i-1;s1,s2);//在HT[k]中选择两个其双亲域为0,且权值最小的结点,并返回他们在HT中的序号s1和s2
    HT[s1].parent=i;//表示从F中删除s1,s2
    HT[s2].parent=i;
    HT[i].lch=s1;
    HT[i].rch=s2;
    HT[i].weigth=HT[s1].weigth+HT[s2].weigth;
  }
}

5.6.4哈夫曼编码

在远程通讯中,要将待传字符转换成由二进制表示的字符串:

学习笔记--霍夫曼树与霍夫曼编码解码_余生相_的博客-CSDN博客_霍夫曼解码

若将编码设计为长度不等的二进制编码,即让待传字符串中出现次数较多的字符采用尽可能短的编码,则转换的二进制字符串便可能减少。

关键:要设计长度不等的编码,则必须使任一字符的编码都不是另一个字符的编码的前缀。——这种编码称做前缀编码。

问题:什么样的前缀码能使得电文总长最短?——哈夫曼编码

  1. 统计字符集中每个字符在电文中出现的平均概率(概率越大,要求编码越短)。
  2. 利用哈夫曼树的特点:权越大的叶子离根越近;将每个字符的概率值作为权值,构造哈夫曼树。则概率越大的结点,路径越短。
  3. 在哈夫曼树的每个分支上标上0或1:
    • 结点的左分支标0,右分支标1
    • 把从根到每个叶子的路径上的标号连接起来,作为该叶子代表的字符的编码。

哈夫曼树 深入剖析 - 知乎

两个问题:

  1. 为什么哈夫曼编码能够保证是前缀编码?

    因为没有一片树叶是另一片树叶的祖先,所以每个叶节点的编码就不可能是其他叶节点编码的前缀。

  2. 为什么哈夫曼编码能够保证字符编码总长最短?

    因为哈夫曼树的带权路径长度最短,故字符编码的总长最短。

哈夫曼编码的性质

  • 性质1:哈夫曼编码是前缀码
  • 性质2:哈夫曼编码是最优前缀码

5.6.5哈夫曼编码的算法实现

C++哈夫曼树+哈夫曼编码的实现(双完整版)_Ac君的博客-CSDN博客_哈夫曼树c++实现

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

5.6.6文件的编码和解码

1、编码

① 输入各字符及其权值

② 构造哈夫曼树——HT[i]

③ 进行哈夫曼编码——HC[i]

④ 查HC[i],得到各字符的哈夫曼编码

2、解码

① 构造哈夫曼树

② 依次读入二进制码

③ 读入0,则走向左孩子;读入1,则走向右孩子

④ 一旦到达某叶子时,即可译出字符

⑤ 然后再从根出发继续译码,直到结束。

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

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

相关文章

【MySQL】MySQL数据类型

文章目录 一、数据类型的分类二、tinyint类型2.1 创建有符号数值2.2 创建无符号数值 三、bit类型三、浮点类型3.1 float3.2 decimal类型 四、字符串类型4.1 char类型4.2 varchar类型 五、日期和时间类型六、枚举和集合类型6.1 enum的枚举值和set的位图结构6.2 查询集合find_in_…

带头单链表,附带完整测试程序

&#x1f354;链表基础知识 1.概念&#xff1a;链表是由多个节点链接构成的&#xff0c;节点包含数据域和指针域&#xff0c;指针域上存放的指针指向下一个节点 2.链表的种类&#xff1a;按单向或双向、带头或不带头、循环或不循环分为多个种类 3.特点&#xff1a;无法直接找到…

信号源功率输出是什么意思(功率信号源)

信号源功率输出是指信号源能够输出的最大功率。在无线电通信和电子工程领域中&#xff0c;信号源功率输出是一项非常重要的参数&#xff0c;它对信号传输距离、接收灵敏度、噪声抑制等方面都有着重要的影响。 信号源功率输出与信号传输距离密切相关。在无线电通信中&#xff0c…

二叉搜索树与双向链表(牛客网 和 剑指 Offer同类型题)

文章目录 JZ36 二叉搜索树与双向链表&#xff08;牛客网&#xff09;剑指 Offer 36. 二叉搜索树与双向链表 JZ36 二叉搜索树与双向链表&#xff08;牛客网&#xff09; 题目链接 输入一棵二叉搜索树&#xff0c;将该二叉搜索树转换成一个排序的双向链表。如下图所示 注意: 1…

亚马逊品牌推荐金计划:通过亚马逊外营销活动赚取奖金!

亚马逊美国站发布公告称新推出的品牌推荐金计划可以让卖家在通过亚马逊外营销活动的销售中获得奖金&#xff0c;当卖家将非亚马逊营销流量引导至亚马逊时&#xff0c;您将获得促销产品以及客户在接下来的两周内购买的任何品牌产品平均销售额的10%的奖金&#xff0c;以下是公告内…

【C语言初阶】使用指针求字符串长度(五个版本盘点总结)

在代码的题目中&#xff0c;我们经常会遇到需要自己手写函数&#xff0c;求字符串长度的情况&#xff0c;那么今天博主就带大家一起盘点五种求字符串长度的写法 版本一&#xff1a;判断累加法 逻辑&#xff1a;由于字符串的末位是\0&#xff0c;且\0不计入字符串长度&#xf…

docker logs 使用说明

docker logs 可以查看某个容器内的日志情况。 前置参数说明 c_name容器名称 / 容器ID logs 获取容器的日志 , 命令如下&#xff1a; docker logs [options] c_name option参数&#xff1a; -n 查看最近多少条记录&#xff1a;docker logs -n 5 c_name--tail与-n 一样 &#…

C高级 作业 day2 8/3

1.脑图 2.递归实现&#xff0c;输入一个数&#xff0c;输出这个数的每一位 #include <myhead.h>void solute(int a) {if(a<10&&a>0) //如果是一位数{printf("%2d\t",a);return;}else //两位及以上{ solute(a/10);printf("%d\t",a%10…

1345:香甜的黄油(Dijkstra)---信息学奥赛一本通

【题目描述】 农夫John发现做出全威斯康辛州最甜的黄油的方法&#xff1a;糖。把糖放在一片牧场上&#xff0c;他知道N&#xff08;1≤N≤500&#xff09;只奶牛会过来舔它&#xff0c;这样就能做出能卖好价钱的超甜黄油。当然&#xff0c;他将付出额外的费用在奶牛上。 农夫Jo…

Electron 开发,报handshake failed; returned -1, SSL error code 1,错误

代码说明 在preload.js代码中&#xff0c;暴露参数给渲染线程renderer.js访问&#xff0c; renderer.js 报&#xff1a;ERROR:ssl_client_socket_impl.cc(978)] failed; returned -1, SSL error code 1,错误 问题原因 如题所说&#xff0c;跨进程传递消息&#xff0c;这意味…

Matlab对TMS320F28335编程--SVPWM配置互补PWM输出

前言 F28335中断 目的&#xff1a;FOC的核心算法及SVPWM输出&#xff0c;SVPWM的载波频率10kHz&#xff0c;SVPWM的每个周期都会触发ADC中断采集相电流&#xff0c;SVPWM为芯片ePWM4、5、6通道&#xff0c;配置死区 1、配置中断SVPWM进ADC中断&#xff0c;查上表知CPU1,PIE1 …

一个计算机专业的学生数据结构这门课学到什么程度才能算学的还不错?

数据结构之所以重要是因为它处于算法中的基础地位&#xff0c;与解决实际问题关系密切&#xff1b;而之所以不重要是因为课本上能学到的所有实现都已经有人造过轮子了&#xff0c;甚至已经作为很多语言的标准API存在了。 换句话来说&#xff0c;在以后的编码生涯中&#xff0c…

Qt tabwidget中插入widget

一、简单介绍 QT->tabWidget&#xff1a;标签页面。 在ui中通过工具栏自定义拉取控件&#xff0c;其中tabwidget可以可以创建多个标签页面&#xff0c;默认生成两个tab_widget(tab_1/tab_2)。并且可以在ui中右键自由添加控制删除等标签页&#xff0c;切换标签页就是切换widg…

TensorRT学习笔记--基于TensorRT部署YoloV3, YoloV5和YoloV8

目录 1--完整项目 2--模型转换 3--编译项目 4--序列化模型 5--推理测试 1--完整项目 以下以 YoloV8 为例进行图片和视频的推理&#xff0c;完整项目地址如下&#xff1a;https://github.com/liujf69/TensorRT-Demo git clone https://github.com/liujf69/TensorRT-Demo.…

Kubernetes高可用集群二进制部署(六)Kubernetes集群节点添加

Kubernetes概述 使用kubeadm快速部署一个k8s集群 Kubernetes高可用集群二进制部署&#xff08;一&#xff09;主机准备和负载均衡器安装 Kubernetes高可用集群二进制部署&#xff08;二&#xff09;ETCD集群部署 Kubernetes高可用集群二进制部署&#xff08;三&#xff09;部署…

8.3 C高级 Shell脚本

写一个脚本&#xff0c;包含以下内容&#xff1a; 显示/etc/group文件中第五行的内容创建目录/home/ubuntu/copy切换工作路径到此目录赋值/etc/shadow到此目录&#xff0c;并重命名为test将当前目录中test的所属用户改为root将test中其他用户的权限改为没有任何权限 #!/bin/b…

1016. 子串能表示从 1 到 N 数字的二进制串;2601. 质数减法运算;1824. 最少侧跳次数

1016. 子串能表示从 1 到 N 数字的二进制串 核心思想&#xff1a;第一种方法&#xff0c;直接将1-n转化为二进制数看它是否是在s中&#xff0c;第二种方法&#xff0c;将s的二进制字串转换为数字存在哈希表中&#xff0c;看它是否满足长度为n,然后具体的方法就是枚举字符串的左…

uC-OS2 V2.93 STM32L476 移植:串口打印篇

前言 前几篇已经 通过 STM32CubeMX 搭建了 NUCLEO-L476RG 的 STM32L476RG 的 裸机工程&#xff0c;下载了 uC-OS2 V2.93 的源码&#xff0c;并把 uC-OS2 的源文件加入 Keil MDK5 工程&#xff0c;通过适配 Systick 系统定时器与 PendSV 实现任务调度&#xff0c;初步让 uC-OS2 …

以技术驱动反欺诈,Riskified 为企业出海保驾护航

如今&#xff0c;全球对于线上消费的需求日益增长&#xff0c;各类新型支付方式也层出不穷。在国内&#xff0c;线上支付有着较为完善的法律及监管条例&#xff0c;格局基本已定型。但对于出海商家而言&#xff0c;由于不同国家和地区的支付规则和监管机制不同&#xff0c;跨境…

老师辅助神器:人工智能知道学生是否在集中注意力

面部识别技术于20世纪60年代引入&#xff0c;在人工智能寒冬中萎靡不振&#xff0c;近年来在日益强大的深度神经网络的推动下已经起飞。面部识别已应用于Face ID设备解锁功能&#xff0c;公共安全服务&#xff0c;智能支付系统等。在泰勒斯威夫特&#xff08;Taylor Swift&…