【数据结构】哈夫曼树

news2025/4/4 21:46:34

哈夫曼树

在学习哈夫曼树之前,先了解以下几个概念:
一:**路径长度:**在一棵树中,从一个节点到另一个节点所经过的“边”的数量,被我们称为两个节点之间的路径长度。
二:**树的路径长度:**这是指从树根到每一个节点的路径长度的总和对于节点数相同的树来说,路径长度最短的是完全二叉树。
三:**节点带权的路径长度:这是指树的根节点到该节点的路径长度和该节点权重的乘积。
:树的带权路径长度:**在一棵树中,所有叶子节点的带权路径长度之和,被称为树的带权路径长度,也被简称为WPL,这是每一个节点的对应的权值乘以对应的路径长度之和。
如图所示:
image-20250330140116571

对于该图:
树路径长度就为1+1+2+2+3+3+4+4=20
A节点带权的路径长度=1*5=5
树的带权路径长度WPL=5×1+15×2+40×3+30×4+10×4=315
我们可以试想,同样的两颗二叉树,一个WPL小,一个WPL大,那么小的一定会在性能上优于大的,所以我们引出另外一个名词——哈夫曼树,即WPL最小的二叉树。
那我们如何才知道一棵树是不是哈夫曼树呢?
构造哈夫曼树:
首先,假如我们有5个节点,它们的权值分别是:A5, E10, B15, D30, C40。
在这些节点中,我们找到权值最小的两个节点,这里是A5和E10。我们将这两个节点合并,生成一个新的节点,新节点的权值是A5和E10的权值之和,即15。这样,我们就得到了新的节点列表:AE15, B15, D30, C40
我们再次在新的节点列表中找到权值最小的两个节点,这次是AE15和B15。我们将这两个节点合并,生成一个新的节点,新节点的权值是AE15和B15的权值之和,即30。这样,我们就得到了新的节点列表:AEB30, D30, C40
我们继续在新的节点列表中找到权值最小的两个节点,这次是AEB30和D30。我们将这两个节点合并,生成一个新的节点,新节点的权值是AEB30和D30的权值之和,即60。这样,我们就得到了新的节点列表:AEBD60, C40。
最后,我们将剩下的两个节点合并,生成一个新的节点,新节点的权值是AEBD60和C40的权值之和,即100。这样,我们就得到了最终的哈夫曼树。
比如这两个图,就是第一步的操作的举例:

image-20250330172845255

当完成后,我们只需要比较一下当前WPL和我们的WPL,若相等,则就是哈夫曼树。
对于一颗哈夫曼树,有:
哈夫曼树中权越大的叶子离根越近
具有相同带权结点的哈夫曼树不唯一
哈夫曼树只有度为0或2的结点,没有度为1的结点
包含n个叶子结点的哈夫曼树共有2n-1个结点(如上图,叶子节点为3,总共2*3-1==5个节点)
n棵二叉树要经历n-1次合并可以形成哈夫曼树
哈夫曼编码:
哈夫曼编码是一种用于无损数据压缩的熵编码(即编码效率最高)算法。它是一种可变字长编码方式,比起定长编码的ASCII编码来说,哈夫曼编码能节省很多的空间,因为每一个字符出现的频率不是一致的。

哈夫曼编码的基本原理和流程如下:

将字符集C作为叶子节点;
将频率集W作为叶子节点的权值;
使用C和W构造哈夫曼树;
对哈夫曼树的所有分支,左子树分支编码为0,右子树分支编码为1;
通过上述流程,即完成了哈夫曼编码。
下面我们来用代码实现一颗哈夫曼树:

首先是结构体的定义:

	struct haftree {
	double weight;//节点权值
	char c;//节点名字
	int lchild, rchild, parent;
};
接下来是各个函数的定义:
1.找出各权值中最小的两个:
void selcet(struct haftree* array, int x, int* m1, int* m2) {
	double min1 = DBL_MAX;
	double min2 = DBL_MAX;
	for (int i = 1; i <= x; i++) {
		if (array[i].weight < min1 && array[i].parent == 0) {//在构建哈夫曼树的过程中,我们需要选择权重最小的两个结点进行合并,构建出新的结点。为了避免重复选择已经合并过的结点,我们需要在选择最小权重结点的时候排除已经合并的结点。因此,需要通过array[i].parent == 0来判断结点是否已经是其他结点的子结点,如果是,则不考虑这个结点。
			min1 = array[i].weight;
			*m1 = i;
		}
	}
	for (int j = 1; j <= x; j++) {
		if (array[j].weight < min2 && j != *m1 && array[j].parent == 0) {
			min2 = array[j].weight;
			*m2 = j;
		}
	}
}

2.构建哈夫曼树:
void createhaftree(struct haftree*array,int n) {
	for (int i = n + 1; i <= 2 * n - 1; i++) {
		int m1,m2;
		Select(array, i - 1, &m1, &m2);
		array[i].weight = array[m1].weight + array[m2].weight;
		array[i].lchild = m1;
		array[i].rchild = m2;
		array[m1].parent = i;
		array[m2].parent = i;
	}
}
3.哈夫曼编码:
void HuffmanCoding(struct haftree* array, int n) {// 一个自定义的数据结构,用于存储哈夫曼树的节点
	char cd[n + 1];
	int start;
	cd[n] = '\0';
	for (int i = 1; i <= n; i++) {
		start = n;
		int c = i;
		int f = array[i].parent;
		while (f != 0) {
			if (array[f].lchild == c)
				cd[--start] = '0';
			else
				cd[--start] = '1';
			c = f;
			f = array[f].parent;
		}
		printf("%s\n", &cd[start]);
	}
}

4.初始化与预处理:
for (int i = 1; i <= n; i++) {
	array[i].weight = weights[i - 1];
	array[i].c = chars[i - 1];
	array[i].parent = 0;
	array[i].lchild = 0;
	array[i].rchild = 0;
}
// 初始化非叶子节点
for (int i = n + 1; i < 2 * n; i++) {
	array[i].weight = 0;
	array[i].c = '\0'; // 非叶子节点没有字符
	array[i].parent = 0;
	array[i].lchild = 0;
	array[i].rchild = 0;
}

完整代码如下:
#include <stdio.h>
#include <float.h>
#include <limits.h>
struct haftree {
	double weight;//节点权值
	char c;//节点名字
	int lchild, rchild, parent;
};
void Select(struct haftree* array, int x, int* m1, int* m2) {
	double min1 = DBL_MAX;
	double min2 = DBL_MAX;
	for (int i = 1; i <= x; i++) {
		if (array[i].weight < min1 && array[i].parent == 0) {
			min1 = array[i].weight;
			*m1 = i;
		}
	}
	for (int j = 1; j <= x; j++) {
		if (array[j].weight < min2 && j != *m1 && array[j].parent == 0) {
			min2 = array[j].weight;
			*m2 = j;
		}
	}
}
void createhaftree(struct haftree* array, int n) {
	for (int i = n + 1; i <= 2 * n - 1; i++) {
		int m1, m2;
		Select(array, i - 1, &m1, &m2);
		array[i].weight = array[m1].weight + array[m2].weight;
		array[i].lchild = m1;
		array[i].rchild = m2;
		array[m1].parent = i;
		array[m2].parent = i;
	}
}
void HuffmanCoding(struct haftree* array, int n) {
	char cd[n + 1];
	int start;
	cd[n] = '\0';
	for (int i = 1; i <= n; i++) {
		start = n;
		int c = i;
		int f = array[i].parent;
		while (f != 0) {
			if (array[f].lchild == c)
				cd[--start] = '0';
			else
				cd[--start] = '1';
			c = f;
			f = array[f].parent;
		}
		printf("%s\n", &cd[start]);
	}
}
int main() {
	int n = 5; // 假设有5个字符
	struct haftree array[2 * n]; // 创建哈夫曼树数组
	double weights[] = { 0.1, 0.15, 0.2, 0.25, 0.3 }; // 假设这是每个字符的权重
	char chars[] = { 'a', 'b', 'c', 'd', 'e' }; // 这是每个字符
// 初始化叶子节点
for (int i = 1; i <= n; i++) {
	array[i].weight = weights[i - 1];
	array[i].c = chars[i - 1];
	array[i].parent = 0;
	array[i].lchild = 0;
	array[i].rchild = 0;
}
// 初始化非叶子节点
for (int i = n + 1; i < 2 * n; i++) {
	array[i].weight = 0;
	array[i].c = '\0'; // 非叶子节点没有字符
	array[i].parent = 0;
	array[i].lchild = 0;
	array[i].rchild = 0;
}
createhaftree(array, n);
HuffmanCoding(array, n);
return 0;
}

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

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

相关文章

HCIP(TCP)(2)

1. TCP三次握手 SYN (同步序列编号) 报文: 客户端发送 SYN 报文&#xff0c;开始建立连接&#xff0c;并初始化序列号。 SYN-ACK (同步序列编号-确认) 报文: 服务器收到 SYN 报文后&#xff0c;回复 SYN-ACK 报文&#xff0c;确认连接请求&#xff0c;并初始化自己的序列号和确…

基于Web的交互式智能成绩管理系统设计

目录 摘要 绪论 一、应用背景 二、行业发展现状 三、程序开发的重要意义 四、结语 1 代码 2 数据初始化模块 3 界面布局模块 4 核心功能模块 5 可视化子系统 6 扩展功能模块 7 架构设计亮点 功能总结 一、核心数据管理 二、智能分析体系 三、可视化系统 四、扩…

k8s日志管理

k8s日志管理 k8s查看日志查看集群中不是完全运行状态的pod查看deployment日志查看service日志进入pod的容器内查看日志 管理k8s组件日志kubectl logs查看日志原理 管理k8s应用日志收集k8s日志思路收集标准输出收集容器中日志文件 k8s查看节点状态失败k8s部署prometheus监控 k8s…

element-plus中,Loading 加载组件的使用

一.基本使用 给一个组件&#xff0c;如&#xff1a;table表格&#xff0c;加上v-loading"true"即可。 举例&#xff1a;复制如下代码。 <template><el-table v-loading"loading" :data"tableData" style"width: 100%"><…

Mybatis_Plus中的常用注解

目录 1、TableName TableId TableId的type属性 TableField 1、TableName 经过以上的测试&#xff0c;在使用MyBatis-Plus实现基本的CRUD时&#xff0c;我们并没有指定要操作的表&#xff0c;只是在 Mapper接口继承BaseMapper时&#xff0c;设置了泛型User&#xff0c;而操…

高并发金融系统,“可观测-可追溯-可回滚“的闭环审计体系

一句话总结 在高并发金融系统中&#xff0c;审计方案设计需平衡"观测粒度"与"系统损耗"&#xff0c;通过双AOP实现非侵入式采集&#xff0c;三表机制保障操作原子性&#xff0c;最终形成"可观测-可追溯-可回滚"的闭环体系。 业务痛点与需求 在…

企业内训|DeepSeek技术革命、算力范式重构与场景落地洞察-某头部券商

3月19日北京&#xff0c;TsingtaoAI公司负责人汶生受邀为某证券公司管理层和投资者举办专题培训&#xff0c;围绕《DeepSeek技术革命、算力范式重构与场景落地洞察》主题&#xff0c;系统阐述了当前AI技术演进的核心趋势、算力需求的结构性变革&#xff0c;以及行业应用落地的关…

VS Code C/C++项目设置launch.json中的environment参数解决支持库路径问题

问题描述 Windows 11 VS Code C/C 开发环境搭建分别写了c和cpp两个示例代码&#xff0c;在运行过程中c代码没有发现问题&#xff08;可能简单&#xff0c;没有用到太多支持&#xff09;&#xff0c;但使用了stl的cpp代码并没有运行出来&#xff0c;如下图&#xff1a; 出问题…

怎样解决 Windows 11 上的 DirectX 错误,最新DX 问题解决方法

在使用 Windows 11 操作系统的过程中&#xff0c;大家可能会遇到 DirectX 错误的情况&#xff0c;这可能会给游戏体验、多媒体应用甚至是系统的整体性能带来负面影响。不过别担心&#xff0c;本文将为大家详细介绍如何解决 Windows 11 上的 DirectX 错误&#xff0c;让您的系统…

PH热榜 | 2025-03-30

1. Deepcord 标语&#xff1a;Discord 数据分析&#xff1a;获取指标洞察与受众研究 介绍&#xff1a;Deepcord&#xff1a;为社区建设者提供的Discord分析工具。跟踪超过50万个服务器的指标&#xff0c;发现热门社区&#xff0c;监控竞争对手&#xff0c;找到你的目标受众。…

Open webui的使用

问题 之前本地量化模型管理器ollama的文章&#xff0c;我们知道可以通过ollama来管理本地量化模型&#xff0c;也能够在命令行中与相关模型进行对话。现在我们想要在有个web页面通过浏览器来与本地模型对话。这里我们就使用Open webui作为界面来与本地模型对话。 安装启动 这…

STM32单片机的桌面宠物机器人(基于HAL库)

效果 基于STM32单片机的桌面宠物机器人 概要 语音模块&#xff1a;ASR PRO&#xff0c;通过天问block软件烧录语音指令 主控芯片&#xff1a;STM32F103C8T6 使用HAL库 屏幕&#xff1a;0.96寸OLED屏&#xff0c;用来显示表情 4个舵机&#xff0c;用来当作四只腿 底部一个面…

Ubuntu 22 Linux上部署DeepSeek R1保姆式操作详解(ollama方式)

操作系统&#xff1a;Ubuntu Linux 22.04 一、安装模型运行环境 打开链接https://ollama.com/download/linux 1.安装ollama &#xff08;1&#xff09;一条指令即可实现的简易版安装方法&#xff08;也可称为在线安装&#xff09; curl -fsSL https://ollama.com/install.s…

深度学习处理时间序列(6)

RNN的高级用法 循环dropout&#xff08;recurrent dropout&#xff09;​&#xff1a;这是dropout的一种变体&#xff0c;用于在循环层中降低过拟合。 循环层堆叠&#xff08;stacking recurrent layers&#xff09;​&#xff1a;这会提高模型的表示能力&#xff08;代价是更…

【鸿蒙5.0】向用户申请麦克风授权

#效果图 步骤 在 config.json 里声明权限&#xff1a;在项目的 config.json 文件中添加麦克风权限的声明&#xff0c;告知系统应用需要使用该权限。检查权限状态&#xff1a;在代码里检查应用是否已经获得了麦克风权限。请求权限&#xff1a;若应用未获得麦克风权限&#xff0…

【解决】导入PNG图片,转 Sprite 格式成功但资产未生效问题

开发平台&#xff1a;Unity 6.0 图片格式&#xff1a;.png   问题描述 当 PNG 成功转换为 Sprite&#xff08;精灵&#xff09;时&#xff0c;资产状态将显示扩展箭头&#xff0c;即表明该资产可 Sprite 使用。 解决方法&#xff1a;设置正确的 Sprite Mode Single 关于 Spr…

从DeepSeek到Qwen,AI大模型的移植与交互实战指南

在不久前发布的《技术实战 | OK3588-C开发板上部署DeepSeek-R1大模型的完整指南》一文中&#xff0c;小编为大家介绍了DeepSeek-R1在飞凌嵌入式OK3588-C开发板上的移植部署、效果展示以及性能评测&#xff0c;本篇文章不仅将继续为大家带来关于DeepSeek-R1的干货知识&#xff0…

SkyWalking+Springboot实战

1、下载SkyWalking APM 1.手动下载 Downloads | Apache SkyWalkinghttps://skywalking.apache.org/downloads/ 2.链接下载 https://dlcdn.apache.org/skywalking/10.2.0/apache-skywalking-apm-10.2.0.tar.gzhttps://dlcdn.apache.org/skywalking/10.2.0/apache-skywalking-…