深入学习二叉树(三) 霍夫曼树

深入学习二叉树(三) 霍夫曼树

1 前言

霍夫曼树是二叉树的一种特殊形式,又称为最优二叉树,其主要作用在于数据压缩和编码长度的优化。

2 重要概念

2.1 路径和路径长度

在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。

在这里插入图片描述
图2.1所示二叉树结点A到结点D的路径长度为2,结点A到达结点C的路径长度为1。

2.2 结点的权及带权路径长度

若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
图2.2展示了一棵带权的二叉树

在这里插入图片描述

2.3 树的带权路径长度

树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。
图2.2所示二叉树的WPL:
WPL = 6 * 2 + 3 * 2 + 8 * 2 = 34;

3 霍夫曼树

3.1 定义

给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为霍夫曼树(Huffman Tree)。
如图3.1所示两棵二叉树
在这里插入图片描述
叶子结点为A、B、C、D,对应权值分别为7、5、2、4。
3.1.a树的WPL = 7 * 2 + 5 * 2 + 2 * 2 + 4 * 2 = 36
3.1.b树的WPL = 7 * 1 + 5 * 2 + 2 * 3 + 4 * 3 = 35
由ABCD构成叶子结点的二叉树形态有许多种,但是WPL最小的树只有3.1.b所示的形态。则3.1.b树为一棵霍夫曼树。

3.2 构造霍夫曼树

构造霍夫曼树主要运用于编码,称为霍夫曼编码。现考虑使用3.1中ABCD结点以及对应的权值构成如下长度编码。
AACBCAADDBBADDAABB。
编码规则:从根节点出发,向左标记为0,向右标记为1。
采用上述编码规则,将图3.1编码为图3.2所示:

在这里插入图片描述
构造过程:
3.1.a所示二叉树称为等长编码,由于共有4个结点,故需要2位编码来表示,编码结果为:
在这里插入图片描述
则AACBCAADDBBADDAABB对应编码为:
00 00 10 01 10 00 00 11 11 01 01 00 11 11 00 00 01 01
长度为36。
3.1.b构造过程如下:
1)选择结点权值最小的两个结点构成一棵二叉树如图3.3:

在这里插入图片描述
2)则现在可以看作由T1,A,B构造霍夫曼树,继续执行步骤1。
选则B和T1构成一棵二叉树如图3.4:

在这里插入图片描述
3)现只有T2和A两个结点,继续执行步骤1。
选择A和T2构成一棵二叉树如图3.5:
在这里插入图片描述
经过上述步骤则可以构造完一棵霍夫曼树。通过观察可以发现,霍夫曼树中权值越大的结点距离根结点越近。
按照图3.5霍夫曼树编码结果:
在这里插入图片描述
则AACBCAADDBBADDAABB对应编码为:
0 0 110 10 110 0 0 111 111 10 10 0 111 111 0 0 10 10
编码长度为35。
由此可见,采用二叉树可以适当降低编码长度,尤其是在编码长度较长,且权值分布不均匀时,采用霍夫曼编码可以大大缩短编码长度。

3.3 代码实现

#include <iostream>
#include <stdlib.h>
using namespace std;
const int MaxValue = 10000;//初始设定的权值最大值
const int MaxBit = 4;//初始设定的最大编码位数
const int MaxN = 10;//初始设定的最大结点个数
struct HaffNode//哈夫曼树的结点结构
{int weight;//权值int flag;//标记int parent;//双亲结点下标int leftChild;//左孩子下标int rightChild;//右孩子下标
};
struct Code//存放哈夫曼编码的数据元素结构
{int bit[MaxBit];//数组int start;//编码的起始下标int weight;//字符的权值
};
void Haffman(int weight[], int n, HaffNode haffTree[])
//建立叶结点个数为n权值为weight的哈夫曼树haffTree
{int j, m1, m2, x1, x2;//哈夫曼树haffTree初始化。n个叶结点的哈夫曼树共有2n-1个结点for (int i = 0; i<2 * n - 1; i++){if (i<n)haffTree[i].weight = weight[i];elsehaffTree[i].weight = 0;//注意这里没打else那{},故无论是n个叶子节点还是n-1个非叶子节点都会进行下面4步的初始化haffTree[i].parent = 0;haffTree[i].flag = 0;haffTree[i].leftChild = -1;haffTree[i].rightChild = -1;}//构造哈夫曼树haffTree的n-1个非叶结点for (int i = 0; i<n - 1; i++){m1 = m2 = MaxValue;//Maxvalue=10000;(就是一个相当大的数)x1 = x2 = 0;//x1、x2是用来保存最小的两个值在数组对应的下标for (j = 0; j<n + i; j++)//循环找出所有权重中,最小的二个值--morgan{if (haffTree[j].weight<m1&&haffTree[j].flag == 0){m2 = m1;x2 = x1;m1 = haffTree[j].weight;x1 = j;}else if(haffTree[j].weight<m2&&haffTree[j].flag == 0){m2 = haffTree[j].weight;x2 = j;}}//将找出的两棵权值最小的子树合并为一棵子树haffTree[x1].parent = n + i;haffTree[x2].parent = n + i;haffTree[x1].flag = 1;haffTree[x2].flag = 1;haffTree[n + i].weight = haffTree[x1].weight + haffTree[x2].weight;haffTree[n + i].leftChild = x1;haffTree[n + i].rightChild = x2;}
}
void HaffmanCode(HaffNode haffTree[], int n, Code haffCode[])
//由n个结点的哈夫曼树haffTree构造哈夫曼编码haffCode
{Code *cd = new Code;int child, parent;//求n个叶结点的哈夫曼编码for (int i = 0; i<n; i++){//cd->start=n-1;//不等长编码的最后一位为n-1,cd->start = 0;//,----修改从0开始计数--morgancd->weight = haffTree[i].weight;//取得编码对应权值的字符child = i;parent = haffTree[child].parent;//由叶结点向上直到根结点while (parent != 0){if (haffTree[parent].leftChild == child)cd->bit[cd->start] = 0;//左孩子结点编码0elsecd->bit[cd->start] = 1;//右孩子结点编码1//cd->start--;cd->start++;//改成编码自增--morganchild = parent;parent = haffTree[child].parent;}//保存叶结点的编码和不等长编码的起始位//for(intj=cd->start+1;j<n;j++)for (int j = cd->start - 1; j >= 0; j--)//重新修改编码,从根节点开始计数--morganhaffCode[i].bit[cd->start - j - 1] = cd->bit[j];haffCode[i].start = cd->start;haffCode[i].weight = cd->weight;//保存编码对应的权值}
}
int main()
{int i, j, n = 4, m = 0;int weight[] = { 2,4,5,7 };HaffNode*myHaffTree = new HaffNode[2 * n - 1];Code*myHaffCode = new Code[n];if (n>MaxN){cout << "定义的n越界,修改MaxN!" << endl;exit(0);}Haffman(weight, n, myHaffTree);HaffmanCode(myHaffTree, n, myHaffCode);//输出每个叶结点的哈夫曼编码for (i = 0; i<n; i++){cout << "Weight=" << myHaffCode[i].weight << "  Code=";//for(j=myHaffCode[i].start+1;j<n;j++)for (j = 0; j<myHaffCode[i].start; j++)cout << myHaffCode[i].bit[j];m = m + myHaffCode[i].weight*myHaffCode[i].start;cout << endl;}cout << "huffman's WPL is:";cout << m;cout << endl;return 0;
}

4 结语

本文主要介绍了霍夫曼树的实际意义和如何构造一棵二叉树。学习霍夫曼树主要是掌握霍夫曼树的构造思想以及构造过程,至于代码实现则是次要的,而且霍夫曼编码实现过程中运用到了贪心算法。

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

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

相关文章

深入学习二叉树(四) 二叉排序树

深入学习二叉树(四) 二叉排序树 1 前言 数据结构中&#xff0c;线性表分为无序线性表和有序线性表。 无序线性表的数据是杂乱无序的&#xff0c;所以在插入和删除时&#xff0c;没有什么必须遵守的规则&#xff0c;可以插入在数据尾部或者删除在数据尾部。但是在查找的时候&a…

MySQL MVCC 概述

文章目录MVCC(Muti Version Concurrency Control) 的概念什么是当前读和快照读背景总结undo 日志InnoDB 中的 MVCCInnoDB 中的 MVCC 与事务隔离级别的关系InnoDB 中的 MVCC 实现原理MVCC(Muti Version Concurrency Control) 的概念 MVCC, 是一种多版本并发控制机制。通过 MVCC…

MySQL 行级锁

MySQL 行级锁的分类 按照锁定的范围不同,行级锁分为: 记录锁:用于锁定指定的某行记录间隙锁:用于锁定指定区间的记录,只有可重复读级别下才有间隙锁,间隙锁不存在冲突, 但是有可能导致死锁。临键锁:用于锁定指定左开右闭区间的记录,是记录锁与间隙锁的结合。

Redis Scan 命令

文章目录1. Scan 命令的基本用法1.1 Scan 命令的输入值1.2 Scan 命令的返回值2. Scan 命令与 Keys 命令比较2.1 Keys 命令的缺点2.2 Scan 命令的优点3. Scan 命令的限制3.1 有限保证原则3.2 返回的结果有可能会重复1. Scan 命令的基本用法 Scan 命令是一个基于游标的迭代器:Sc…

MybatisPlus 的 MetaObjectHandler 与 @TableLogic

文章目录1.MetaObjectHandler 实现公共字段自动填充功能1.1 日常开发中的公共字段1.2 Mybatis Plus 中的解决方案1.3 用法1.3.1 定义公共字段超类,并在字段上添加注解1.3.2 实现 MetaObjectHandler 接口2. Mybatis Plus 实现逻辑删除2.1 目前的逻辑删除2.2 Mybatis Plus 提供的…

红黑树 —— 原理和算法详细介绍

红黑树 —— 原理和算法详细介绍 R-B Tree简介 R-B Tree&#xff0c;全称是Red-Black Tree&#xff0c;又称为“红黑树”&#xff0c;它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色&#xff0c;可以是红(Red)或黑(Black)。 红黑树的特性: 每个节点或…

微服务雪崩效应与 Hystrix

文章目录微服务雪崩效应微服务中常见的容错方案常见的服务容错思路Hystrix 简介微服务雪崩效应 微服务系统中, 每一个服务专心于自己的业务逻辑, 并对外提供相应的接口, 看上去似乎耦合度比较低, 但经常会遇见这样一种场景: 可以看到, 当 C 服务挂掉时, B 服务还在不断地调用…

MySQL count(1) , count(*), count(列名) 的异同

count 函数主要用于统计行数,我们一般会用 count(1) , count(*), count(列名) 来统计行数,但是这三者之间有什么差距呢? 异同 当 count(列名) 在列名不是主键列的情况下,将只统计该列的非空行数,效率也最低下;当 count(列名) 在列名为主键列,且在没有非聚集索引的情况下,三者…

时间复杂度到底怎么算

时间复杂度到底怎么算 算法&#xff08;Algorithm&#xff09;是指用来操作数据、解决程序问题的一组方法。对于同一个问题&#xff0c;使用不同的算法&#xff0c;也许最终得到的结果是一样的&#xff0c;但在过程中消耗的资源和时间却会有很大的区别。 那么我们应该如何去衡…

Java 异常种类及处理方法

概述 异常的基类是 Throwable, Throwable 有两个子类: Exception : 表示可以恢复的异常, 编译器可以捕捉。Error : 表示编译时和系统错误, 表示系统在运行期间出现了严重的错误, 属于不可恢复的错误。 受检异常和非受检异常 受检异常指的是在编译期间会接受编译器检查, 且必…

十分钟搞定时间复杂度(算法的时间复杂度)

十分钟搞定时间复杂度&#xff08;算法的时间复杂度&#xff09; 我们假设计算机运行一行基础代码需要执行一次运算。 int aFunc(void) {printf("Hello, World!\n"); // 需要执行 1 次return 0; // 需要执行 1 次 }那么上面这个方法需要执行 2 次运算 …

Java 父子类加载顺序

现在有一个Parent类和一个Son类, 代码分别如下: public class Parent {static {System.out.println(" order 0");}private static String s init();{System.out.println(" order 3");}public Parent() {System.out.println(" order 4");}publi…

java实现简单二叉树

二叉树基本知识&#xff1a; 一、树的定义 树是一种数据结构&#xff0c;它是由n&#xff08;n>1&#xff09;个有限结点组成一个具有层次关系的集合。 树具有的特点有&#xff1a; &#xff08;1&#xff09;每个结点有零个或多个子结点 &#xff08;2&#xff09;没有…

HashMap 学习笔记

1.HashMap 的类继承关系 图示即为 Map 相关类的继承关系。源码中的类签名如下: public class HashMap<K,V> extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable {...... }2.HashMap 的底层存储结构 HashMap 的底层存储结构是 Node 类,…

MySQL中清空表和截断表的区别(新手入门)

清空表和截断表 清空表&#xff1a;delete from users&#xff1b; 清空表只是清空表中的逻辑数据&#xff0c;但是物理数据不清除&#xff0c;如主键值、索引等不被清除&#xff0c;还是原来的值。 截断表&#xff1a;truncate table users&#xff1b; 截断表可以用于删除…

终止线程的方法

如何终止一个正在运行的线程&#xff1f; 设置状态位来终止一个正在运行的线程。可以自己实现, 也可以使用 interrupt 方法来设置这个状态位, 然后在代码中判断 isInterrupted 的返回结果来执行退出执行的逻辑。 了解 Thread 类中的 stop、interrupt 方法吗?为什么不用 stop…

十大经典排序算法动画与解析(配代码完全版)

排序算法是《数据结构与算法》中最基本的算法之一。 排序算法可以分为内部排序和外部排序。 内部排序是数据记录在内存中进行排序。 而外部排序是因排序的数据很大&#xff0c;一次不能容纳全部的排序记录&#xff0c;在排序过程中需要访问外存。 常见的内部排序算法有&…

服务启动不了,显示 config 异常的问题排查

文章目录问题详情排查过程1.1 查看配置文件是否可以正常加载1.2 进入 config 服务正在运行的容器, 查看文件是否存在1.3 查看容器运行日志:最后发现是配置文件中多了一个 TAB 符,唉,说多了都是泪!问题详情 出现异常报错: Could not locate PropertySource and the fail fast p…

java使用Socket类接收和发送数据

java使用Socket类接收和发送数据 网络应用分为客户端和服务端两部分&#xff0c;而Socket类是负责处理客户端通信的Java类。通过这个类可以连接到指定IP或域名的服务器上&#xff0c;并且可以和服务器互相发送和接受数据。在本文及后面的数篇文章中将详细讨论Socket类的使用&a…

Docker Swarm compose 文件 depends_on 属性

Swarm 部署时候如果需要管理应用之间的启动先后顺序,则可以使用 services.depends_on 属性进行指定,例如 services:eureka-service:......depends_on:- config-service......