最优二叉树(哈夫曼树)

news2024/11/15 21:24:12

一、最优二叉树

1、定义

官方定义:在权值为w1,w2,…,wn的 n个叶子所构成的所有二叉树中,带权路径长度最小(即代价最小)的二叉树称为最优二叉树或哈夫曼树。

通俗来讲,就是给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树(Full Binary Tree)或者哈夫曼树(Huffman Tree)。

哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

2、几个概念

2.1 路径长度

路径是从树中一个结点到另一个结点之间的通路,路径上的分支数(边)称为它的路径长度

2.2 树的路径长度

树的路径长度是从树根到每一叶子结点之间的路径长度之和。

2.3 树的带权路径长度(WPL)

树的带权路径长度(Weighted Path Length of Tree,简记为WPL):定义为树中所有叶子结点的带权路径长度之和.

  • 结点的权值:在一些应用中,赋予树中结点的一个有某种意义的实数。
  • 结点的带权路径长度:从该结点到树根之间的路径长度与该结点上权值的乘积。

2.4 示例:计算WPL

计算下面三颗二叉树的带权路径长度总和,其中每个点的权重为:a(7), b(5), c(2), d(4)。

在这里插入图片描述

WPL(a)7*2 + 5*2 + 2*2 + 4*2 = 36
WPL(b)7*3 + 5*3 + 2*1 + 4*2 = 46
WPL(c)7*1 + 5*2 + 2*3 + 4*3 = 35

其中 ©树的 WPL最小,可以验证,它就是哈夫曼树。

注意:

  • 叶子上的权值均相同时,完全二叉树一定是最优二叉树,否则完全二叉树不一定是最优二叉树。
  • 最优二叉树中,权越大的叶子离根越近。
  • 最优二叉树的形态不唯一,WPL最小。

3、哈夫曼编码

任一字符的编码都不是另一个字符的编码的前缀,这种编码称为哈夫曼编码,也称为前缀码

我们可以:

  • 根据所提供的字母数据建立一个 Huffman树;
  • 根据生成的 Huffman树的结构,显示输出所有字母的Huffman编码。

比如:在上图的最优二叉树中我们给每一条边加上一个权值,指向左子节点的边我们标记为0,指向右子节点的边标记为1,那从根节点到叶节点的路径就是我们说的哈夫曼编码。

所以图c的赫夫曼树对应的编码就是:

a:0
b:10
c:110
d:111

4、哈夫曼算法

对于给定的叶子数目及其权值构造最优二叉树的方法,由于这个算法是哈夫曼提出来的,故称其为哈夫曼算法。

其基本思想是:

(1)根据给定的n个权值w1,w2,…,wn构成n棵二叉树的森林F={T1,T2,…,Tn},其中每棵二叉树Ti中都只有一个权值为wi的根结点,其左右子树均空。

(2)在森林F中选出两棵根结点权值最小的树(当这样的树不止两棵树时,可以从中任选两棵),将这两棵树合并成一棵新树,为了保证新树仍是二叉树,需要增加一个新结点作为新树的根,并将所选的两棵树的根分别作为新根的左右孩子(谁左,谁右无关紧要),将这两个孩子的权值之和作为新树根的权值。

(3)对新的森林F重复(2),直到森林F中只剩下一棵树为止。这棵树便是哈夫曼树。

注意:

  • 初始森林中的n棵二叉树,每棵树有一个孤立的结点,它们既是根,又是叶子
  • n个叶子的哈夫曼树要经过n-1次合并,产生n-1个新结点。最终求得的哈夫曼树中共有2n-1个结点。
  • 哈夫曼树是严格的二叉树,没有度数为1的分支结点。

4.1 核心思想和实现思路

核心思想:

使用贪心算法:利用局部最优推出全局最优,把频率出现多的用短码表示,频率出现小的就用长一点。而且,任何一个字符的编码都不是另一个的前缀,在解压缩的时候,我们每次会读取尽可能长的可解压的二进制串,所以在解压缩的时候也不会产生歧义。

具体实现思路:

  1. 每次取数值最小的两个节点,将之组成为一颗子树。
  2. 移除原来的两个点
  3. 然后将组成的子树放入原来的序列中
  4. 重复执行1 2 3 直到只剩最后一个点

二、代码实现

1、示例

例子: a:3 b:24 c:6 d:20 e:34 f:4 g:12

根据以上权重来实现哈夫曼树。

1)数据结点

public class HuffmanNode implements Comparable<HuffmanNode> { // 优先队列,小的我把你优先级调高

	String chars; // 节点里面的字符
	int fre; // 表示是频率
	HuffmanNode left;
	HuffmanNode right;
	HuffmanNode parent; // 用来找上层的

	@Override
	public int compareTo(HuffmanNode o) {
		return this.fre - o.fre;
	}
	
}

2)哈夫曼树

public class HuffmanTree {

	HuffmanNode root;
	List<HuffmanNode> leafs; // 叶子节点
	Map<Character, Integer> weights; // 叶子节点的权重, a,b,c,d,e

	public HuffmanTree(Map<Character, Integer> weights) {
        this.weights = weights;
        leafs = new ArrayList<HuffmanNode>();
    }

	/**
	 * 叶子节点进行编码
	 * 
	 * @return
	 */
	public Map<Character, String> code() {

		Map<Character, String> map = new HashMap<Character, String>();
		for (HuffmanNode node : leafs) {
			String code = "";
			Character c = new Character(node.chars.charAt(0)); // 叶子节点肯定只有一个字符
			HuffmanNode current = node; // 只有一个点
			do {
				if (current.parent != null && current == current.parent.left) { // 说明当前点是左边
					code = "0" + code;
				} else {
					code = "1" + code;
				}
				current = current.parent;
			} while (current.parent != null); // parent == null就表示到了根节点
			map.put(c, code);
		}
		return map;

	}

	/**
	 * 生成哈夫曼树
	 */
	public void creatTree() {
		Character keys[] = weights.keySet().toArray(new Character[0]); // 拿出所有的点
		/**
		 * jdk底层的优先队列
		 */
		PriorityQueue<HuffmanNode> priorityQueue = new PriorityQueue<HuffmanNode>(); 
		for (Character c : keys) {
			HuffmanNode hfmNode = new HuffmanNode();
			hfmNode.chars = c.toString();
			hfmNode.fre = weights.get(c); // 权重
			priorityQueue.add(hfmNode); // 首先把我们的优先队列初始化进去
			leafs.add(hfmNode);
		}

		int len = priorityQueue.size();
		for (int i = 1; i <= len - 1; i++) { // 每次找最小的两个点合并
			HuffmanNode n1 = priorityQueue.poll(); //
			HuffmanNode n2 = priorityQueue.poll(); // 每次取优先队列的前面两个 就一定是两个最小的

			HuffmanNode newNode = new HuffmanNode();
			newNode.chars = n1.chars + n2.chars; // 我们把值赋值一下,也可以不复制
			newNode.fre = n1.fre + n2.fre; // 把权重相加

			// 维护出树的结构
			newNode.left = n1;
			newNode.right = n2;
			n1.parent = newNode;
			n2.parent = newNode;

			priorityQueue.add(newNode);
		}
		root = priorityQueue.poll(); // 最后这个点就是我们的根节点
		System.out.println("构建完成");
	}

}

3)测试

	public static void main(String[] args) {
		/**
		 * 示例:a:3 b:24 c:6 d:20 e:34 f:4 g:12
		 */
		Map<Character, Integer> weights = new HashMap<Character, Integer>();
		// 初始化密码本
		weights.put('a', 3);
		weights.put('b', 24);
		weights.put('c', 6);
		weights.put('d', 1);
		weights.put('e', 34);
		weights.put('f', 4);
		weights.put('g', 12);

		HuffmanTree huffmanTree = new HuffmanTree(weights);
		huffmanTree.creatTree();
		Map<Character, String> code = huffmanTree.code();

		code.forEach((k, v) -> {
			System.out.println(k + ":" + v);
		});

	}

在这里插入图片描述

– 求知若饥,虚心若愚。

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

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

相关文章

仿牛客论坛项目总结

一.数据库中每一张表有哪些字段 user表 用户表 &#xff08;1&#xff09;id 用户的id &#xff08;2&#xff09; username 用户名 &#xff08;3&#xff09;password 密码 &#xff08;4&#xff09;salt 盐 &#xff08;5&#xff09;emai邮箱 &#xff08;6&…

PAT甲级1008 Elevator C++/C语言

1008 Elevator 分数 20 作者 CHEN, Yue 单位 浙江大学 The highest building in our city has only one elevator. A request list is made up with N positive numbers. The numbers denote at which floors the elevator will stop, in specified order. It costs 6 seconds …

联邦学习将如何影响您的日常生活?

人工智能 (AI) 被认为是下一次工业革命的最大创新之一&#xff0c;其中包括机器学习。另一方面&#xff0c;随着原油和电力成为现代工业的基础资源&#xff0c;数据成为人工智能和机器学习的关键要素。 数据隐私与需求之间的冲突 训练的数据样本的大小决定了可用于增强 AI 性能…

CPT203-Software Engineering(2)

文章目录5. Scrum Framework5.1 Scrum Roles5.2 Scrum Activities and Artifacts6. Requirements Engineering6.1 User requirements and system requirements6.2 Functional & Non-functional requirements6.2.1 Functional requirements6.2.2 Non-functional requirement…

第一章:C++算法基础之基础算法

系列文章目录 文章目录系列文章目录前言一、排序&#xff08;1&#xff09;快速排序核心思想思路分析模板&#xff08;2&#xff09;归并排序核心思想思路分析模板稳定性时间复杂度二分查找&#xff08;1&#xff09;整数二分核心思想思路分析模板&#xff08;2&#xff09;浮点…

jetson nano系统引导安装(无外设安装方式)

文章目录一.硬件设置二.系统设置一.硬件设置 插入烧写好系统的SD卡将micro USB线接到jetson nano上&#xff0c;另一端USB A接到电脑上为jetson nano插入电源&#xff0c;开机等待电脑检测到如下盘符说明jetson nano连接成功 二.系统设置 进入电脑的设备管理器&#xff0c;查…

【linux】三种权限的使用和更改、粘滞位和yum的使用

目录 1.权限问题 ①什么是权限&#xff1f; ②小问题 ③默认权限 ④如何更改“人”的权限呢&#xff1f; ⑤更改权限的八进制方案 ⑥强制改权限里的“人”&#xff08;权限人文件属性&#xff09; 2.粘滞位 2.yum的使用 1.权限问题 ①什么是权限&#xff1f; 权限人&a…

HTTP协议解析

HTTP概述 HTTP (全称为 "超文本传输协议") 是一种应用非常广泛的应用层协议~~我们平时打开一个网站, 就是通过 HTTP 协议来传输数据的。 HTTP工作过程&#xff1a; 当我们在浏览器中输入一个 "网址"&#xff0c;此时浏览器就会给对应的服务器发送一个 H…

CTF中的PHP特性函数(上)

前言 对于PHP大家一定不陌生&#xff0c;但你知道PHP在CTF中是如何考察的吗&#xff0c;本文给大家带来的是通过PHP特性来进行CTF比赛中解题出题的知识&#xff0c;会介绍一下CTF中常见的php特性以及围绕该知识点的相关案例&#xff0c;因为内容过多这里分成上中下三篇来讲&am…

操作系统的特征

文章目录&#x1f380;前言&#xff1a;本篇博客知识总览&#x1f354;并发&#x1f387;概念&#xff1a;&#x1f354;共享&#x1f387;概念&#xff1a;&#x1f354;虚拟&#x1f387;概念&#xff1a;&#x1f354;异步&#x1f387;概念&#xff1a;&#x1f3f3;️‍&a…

ThinkPHP 多应用模式之Api路由分组+中间件

ThinkPHP 6.1 在多应用模式下实现API路由分组中间件验证业务 目录 1.创建中间件文件 2.迁移中间件到子应用目录中 3.编辑中间件验证业务 修改命名空间 编写handle处理代码 4.注册中间件 编辑中间件文件 TP内置中间件 5.设置路由分组 优化相同控制器前缀 最终效果&am…

【信息论与编码 沈连丰】第六章:连续信息和连续信道

【信息论与编码 沈连丰】第六章&#xff1a;连续信息和连续信道第六章 连续信息和连续信道6.1 连续消息的信息6.2 连续消息在信道上的传输问题6.3 香农信道容量公式6.4 连续消息的识别和理想接收机6.5 连续信源的数字处理及其编码第六章 连续信息和连续信道 6.1 连续消息的信息…

在 KubeSphere 上部署 OpenLDAP 并进行对接使用

在 KubeSphere 上部署 OpenLDAP 并进行对接-进阶背景前置条件KubeSphere 中部署 LDAP部署 LDAP 应用ApacheDirectoryStudio 验证 LDAP下载部署 ApacheDirectoryStudioApacheDirectoryStudio 测试 LDAP创建 Ldap Search &#xff0c;KS 对接时可选择使用KubeSphere 对接 LDAPHar…

【区块链 | EVM】深入理解学习EVM - 深入Solidity数据存储位置:内存

图片来源: Mech Mind on Unsplash 这是深入Solidity数据存储位置系列的另一篇。在今天的文章中,我们将学习EVM内存的布局,它的保留空间,空闲内存指针,如何使用memory引用来读写内存,以及使用内存时的常规最佳做法。 我们将使用 Ethereum Name Service (ENS)中的合约代码…

实模式和保护模式的区别

实模式和保护模式的区别 实模式和保护模式的来历 最早期的8086 CPU只有一种工作方式 ---- 实模式。数据总线为16位&#xff0c;地址总线为20位。实模式下所有寄存器都是16位。 从80286开始就有了保护模式&#xff0c;从80386开始CPU数据总线和地址总线均为32位&#xff0c;而且…

开发板测试手册——SPI FLASH 读写、USB WIFI 模块(2)

目录 1.8 SPI FLASH 读写测试 20 1.9 USB 接口读写测试 21 1.10 网络接口测试 23 1.10.1 网络连通测试 23 1.10.2 网络速度测试 25 2 网络静态 IP 设置 27 3 USB WIFI 模块测试 31 3.1 WIFI STA 功能测试 32 3.2 WIFI AP 功能测试 35 3.3 USB WIFI 驱动编译 39 前 言…

scikit-learn线性模型之线性回归

scikit-learn线性模型之线性回归线性回归参考文献线性回归 有监督学习中主要解决两个问题&#xff0c;一个是分类&#xff0c;另一个是回归。 在回归问题中&#xff0c;我们需要利用我们已知的特征 x1,x2,...,xpx_1,x_2,...,x_px1​,x2​,...,xp​ 去预测我们的目标变量 yyy 。…

隐私计算一体机,金融大数据规模商用的催化剂

一股隐私计算的浪潮正席卷金融行业。 银行通过隐私计算引入外部不动产数据&#xff0c;与行内贷款企业的时点贷款余额、注册资本等数据联合建立企业贷中预警监测模型&#xff0c;提升银行风险监测业务能力&#xff1b;银行利用联邦学习与互联网公司的客户特征数据完成联合建模…

Linux:使用telnet命令提示:Connection refused

我是 ABin-阿斌&#xff1a;写一生代码&#xff0c;创一世佳话&#xff0c;筑一览芳华。如果小伙伴们觉得不错就一键三连吧~ 下一篇&#xff1a;Linux安装telnet命令教程 文章目录一、分析没有xinetd服务&#xff1a;二、讲解&#xff1a; 什么是 telnet 命令具体语法具体参数三…

Node.js--》如何在Node.js中操作MySQL

目录 数据库 MySQL的安装与配置 SQL管理数据库 WHERE子句 AND和OR ORDER BY子句 COUNT(*)函数 在Express项目中操作MySQL 数据库 数据库(database)是用来组织、存储和管理数据的仓库。当今世界是一个充满着数据的互联网世界&#xff0c;充斥着大量的数据。数据的来源有…