算法学习笔记(7.6)-贪心算法(霍夫曼编码)

目录

1.什么是霍夫曼树

2.霍夫曼树的构造过程

3.霍夫曼编码

3.1具体的作用-频率统计

##实战题目


1.什么是霍夫曼树

给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

相关知识:
1、路径和路径长度
在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。
通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
2、结点的权及带权路径长度
若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。
结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
3、树的带权路径长度
树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。

2.霍夫曼树的构造过程

哈夫曼树的节点个数:
哈夫曼树是一种满二叉树,这意味着除了叶节点外,每个节点都有两个子节点。对于 (N) 个唯哈夫曼树的节点个数与输入的唯一字符数量有关。设字符集合的大小为 (N)(即有 (N) 个唯一字符),则哈夫曼树的节点数最小值和最大值如下:一字符,会有 (N) 个叶节点,每个叶节点代表一个字符。最小值
最小节点数:当输入的字符集合大小为 (N) 时,最小的哈夫曼树(也是理论上的最小值)会有 (2N - 1) 个节点。这是因为在构建哈夫曼树时,每次合并两个节点会创建一个新的内部节点,而最终只剩下一个节点(根节点)没有被合并。这意味着有(N-1) 次合并操作,每次合并操作增加一个新的内部节点,因此有 (N-1) 个内部节点和 (N) 个叶节点,总共 (2N - 1) 个节点。最大值
对于哈夫曼树,最大节点数的概念实际上并不适用,因为哈夫曼树的构建是基于字符的频率的,其目的是为了创建一个尽可能高效的编码方案。每个唯一字符都会成为树的一个叶节点,而内部节点的数量总是 (N-1),所以节点总数固定为 (2N - 1)。不会有比这更多的节点数,因为这是根据哈夫曼算法的定义和工作方式所固有的。结论
对于 (N) 个唯一字符,哈夫曼树的节点数总是 (2N - 1),这里没有最大值概念,因为节点总数是固定的,只要给定了唯一字符的数量。

哈夫曼树的构造过程:

假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:

(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);

(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;

(3)从森林中删除选取的两棵树,并将新树加入森林;

(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。

3.霍夫曼编码

哈夫曼编码是一种压缩编码的编码算法,是基于哈夫曼树的一种编码方式。哈夫曼树又称为带权路径长度最短的二叉树。

哈夫曼编码跟 ASCII 编码有什么区别?

ASCII 编码是对照ASCII 表进行的编码,每一个字符符号都有对应的编码,其编码长度是固定的。而哈夫曼编码对于不同字符的出现频率其使用的编码是不一样的。其会对频率较高的字符使用较短的编码,频率低的字符使用较高的编码。这样保证总体使用的编码长度会更少,从而实现到了数据压缩的目的。

3.1具体的作用-频率统计

哈夫曼树是一个带权的二叉树,而在哈夫曼编码中,字符的出现频率就是字符的权重。因此要根据字符的频率放入优先队列中进行排序。然后根据这些字符构建一棵哈夫曼树

##代码实现

//c++代码实现
#include <iostream>
#include <vector>
using namespace std;
#include <algorithm> // 包含<algorithm>以使用reverse函数
#define MAXCODELEN 100
#define MAXHAFF 256 // 为了简化,假设最多处理256个不同的字符
#define MAXWEIGHT 10000typedef struct Haffman {//权重 int weight;//字符 char ch;//父节点,左儿子,右儿子 int parent, leftChild, rightChild;
} HaffmanNode;typedef struct Code {int code[MAXCODELEN];int start;
} HaffmanCode;HaffmanNode haffman[MAXHAFF * 2 - 1]; // 扩展数组大小以容纳所有可能的内部节点
HaffmanCode code[MAXHAFF];//构造哈夫曼树的过程 
void buildHaffman(int all) {//对每个节点进行初始化 //哈夫曼节点的个数总为 2 * all - 1 for (int i = 0; i < 2 * all - 1; i++) {haffman[i].weight = 0;haffman[i].parent = -1;haffman[i].leftChild = -1;haffman[i].rightChild = -1;}cout << "请输入需要哈夫曼编码的字符和权重大小" << endl;for (int i = 0; i < all; i++) {//获取每个字符及权重 cout << "请分别输入第" << i + 1 << "个哈夫曼字符和权重:" << endl;cin >> haffman[i].ch >> haffman[i].weight;}//用来存储两个最小权值的节点以及权重 int x1, x2, w1, w2;//all个节点,只需要合并all - 1 次,循环all - 1 次即可 for (int i = 0; i < all - 1; i++) {//初始化成不可能的值 x1 = x2 = -1;w1 = w2 = MAXWEIGHT;//遍历每一个可能的节点,包括初始的叶节点和已经创建的内部节点。 for (int j = 0; j < all + i; j++) {if (haffman[j].parent == -1) {//如果当前节点没有父节点 ,即还没有加入到haffman树中,即考虑这个节点 if (haffman[j].weight < w1) {w2 = w1;x2 = x1;//利用x1,和w1来接受第一个节点和权值 w1 = haffman[j].weight;x1 = j;}//利用x2,和w2来接受第二个节点和权值 else if (haffman[j].weight < w2) {w2 = haffman[j].weight;x2 = j;}}}//循环遍历一遍后,x1,x2,w1,w2即是最小两个节点的选择和权值 //将其加入到huffman树中 haffman[all + i].leftChild = x1;haffman[all + i].rightChild = x2;haffman[all + i].weight = w1 + w2;haffman[x1].parent = all + i;haffman[x2].parent = all + i;}
}void printCode(int all) {//遍历每一个字符-我们所输入的 for (int i = 0; i < all; i++) {//用来存储每一个字符的haffman编码 vector<int> hCode;int current = i;int parent = haffman[current].parent;//通过回溯到根节点,然后确保haffman编码的完整性 while (parent != -1) {//左孩子 '0' if (haffman[parent].leftChild == current) {hCode.push_back(0);}//右孩子 '1' else {hCode.push_back(1);}current = parent;parent = haffman[current].parent;}reverse(hCode.begin(), hCode.end());cout << haffman[i].ch << ": Huffman Code is: ";for (int j = 0 ; j < hCode.size() ; j++){cout << hCode[j] ;}	
//        for (int bit : hCode) {
//            cout << bit;
//        }cout << endl;}
}int main() {cout << "请输入有多少个哈夫曼字符:" << endl;int all = 0;cin >> all;if (all <= 0) {cout << "你输入的个数有误" << endl;return -1;}buildHaffman(all);printCode(all);return 0;
}

#python代码
import heapq
from collections import defaultdict, Counter# 定义树的节点
class Node:def __init__(self, char, freq):self.char = charself.freq = freqself.left = Noneself.right = None# 为了让节点能在优先队列中按频率排序,定义比较方法def __lt__(self, other):return self.freq < other.freq# 构建哈夫曼树
def build_huffman_tree(text):# 统计字符频率frequency = Counter(text)# 创建优先队列priority_queue = [Node(char, freq) for char, freq in frequency.items()]heapq.heapify(priority_queue)# 循环直到队列中只剩一个元素while len(priority_queue) > 1:# 弹出两个最小元素left = heapq.heappop(priority_queue)right = heapq.heappop(priority_queue)# 创建新的内部节点,将两个节点作为子节点merged = Node(None, left.freq + right.freq)merged.left = leftmerged.right = right# 将新节点加入优先队列heapq.heappush(priority_queue, merged)# 队列中剩下的元素就是树的根节点return priority_queue[0]# 生成哈夫曼编码
def generate_codes(node, prefix="", code={}):if node is not None:if node.char is not None:code[node.char] = prefixgenerate_codes(node.left, prefix + "0", code)generate_codes(node.right, prefix + "1", code)return code# 编码文本
def encode(text, codes):return ''.join(codes[char] for char in text)# 解码文本
def decode(encoded_text, root):decoded_text = ""current_node = rootfor bit in encoded_text:if bit == '0':current_node = current_node.leftelse:current_node = current_node.rightif current_node.char is not None:decoded_text += current_node.charcurrent_node = rootreturn decoded_text# 主函数
# def main():
#     text = "this is an example for huffman encoding"
#     root = build_huffman_tree(text)
#     codes = generate_codes(root)
#     encoded_text = encode(text, codes)
#     decoded_text = decode(encoded_text, root)
#
#     print(f"Original text: {text}")
#     print(f"Encoded text: {encoded_text}")
#     print(f"Decoded text: {decoded_text}")# if __name__ == "__main__":
#     main()# 主函数
def main():text = "this is an example for huffman encoding"root = build_huffman_tree(text)codes = generate_codes(root)# 打印每个字符的哈夫曼编码print("Huffman Codes for each character:")for char, code in codes.items():print(f"'{char}': {code}")encoded_text = encode(text, codes)decoded_text = decode(encoded_text, root)print(f"\nOriginal text: {text}")print(f"Encoded text: {encoded_text}")print(f"Decoded text: {decoded_text}")if __name__ == "__main__":main()

##实战题目

3531. 哈夫曼树 - AcWing题库

##示例代码

//c++代码
#include <iostream>
#include <queue>
using namespace std;int n, x, ans;
priority_queue <int, vector <int>, greater <int> > q;int main ()
{cin >> n;for (int i = 1; i <= n; i ++){cin >> x, q.push (x); // 加入二叉堆(优先队列)}while (-- n){x = q.top (), q.pop (), x += q.top (), q.pop (); // 取出两个堆顶(当前最小值)合并ans += x, q.push (x); // 统计答案,在放进堆中}cout << ans;return 0;
}

 148. 合并果子 - AcWing题库

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

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

相关文章

ast.js是什么?

在devtools分析网站时&#xff0c;出现了ast.js的页面。那么&#xff0c;什么是ast.js?它有什么用&#xff1f; 经查询&#xff0c;AST是抽象语法树&#xff08;Abstract Syntax Tree&#xff09;也称为AST语法树&#xff0c;指的是源代码语法所对应的树状结构。也就是说&…

vue3+uniapp

1.页面滚动 2.图片懒加载 3.安全区域 4.返回顶部&#xff0c;刷新页面 5.grid布局 place-self: center; 6.模糊效果 7.缩放 8.微信小程序联系客服 9.拨打电话 10.穿透 11.盒子宽度 12.一般文字以及盒子阴影 13.选中文字 14.顶部安全距离 15.onLoad周期函数在setup语法糖执行后…

论文阅读:Neural Scene Flow Prior

目录 概要 Motivation 整体架构流程 技术细节 小结 论文地址:

基于GTX 8B10B编码的自定义PHY上板测试(高速收发器十四)

前文整理了GTX IP&#xff0c;完成了自定义PHY协议的收发模块设计&#xff0c;本文将通过光纤回环&#xff0c;对这些模块上板测试&#xff0c;首先需要编写一个用于生成测试数据的用户模块。 1、测试数据生成模块 本模块用于生成自定义PHY协议的测试数据&#xff0c;通过axi_…

期望18K,4年前端Cvte 视源股份一面挂

一面 1、自我介绍&#xff1f;毕业的时候一直在 xx 公司&#xff0c;你基本都在做什么项目&#xff1f; 2、你讲一下你主要负责哪一块的&#xff1f;balabala 3、你们的 json 是怎么定义组件间的联动的&#xff1f; 4、怎么确定区分两个 input&#xff1f; 5、你们是怎么触…

软件三班20240605

文章目录 1.创建工程和模块2.添加 web支持3.创建前端代码4.添加servlet 依赖5. 代码6.案例2 1.创建工程和模块 2.添加 web支持 方法1 方法2 3.创建前端代码 4.添加servlet 依赖 5. 代码 <!DOCTYPE html> <html lang"en"> <head><meta c…

Spring Boot中的JDK 线程池以及Tomcat线程池使用与配置

Spring Boot中的线程池使用与配置 一、引言 在Java开发中&#xff0c;特别是在高并发场景下&#xff0c;线程池是一个非常重要的概念。合理地使用线程池可以优化系统性能&#xff0c;避免线程频繁创建和销毁带来的性能开销。Spring Boot作为当前最流行的Java框架之一&#xf…

VBA模拟题库生成器

VBA模拟题库生成器 Option ExplicitDim intXZ As Integer 选择题数 Dim intPD As Integer 判断题数 Dim intTK As Integer 填空题数 Dim intWD As Integer 问答题数 Dim intNon As Integer 未知题数 Dim intCount As Integer 题库行数 Dim intCol As Integer …

【Nacos源码分析02-服务配置】

文章目录 服务配置Nacos Config入门Nacos服务端配置发布源码Nacos 服务端监控源码 服务配置 服务配置中心介绍 首先我们来看一下,微服务架构下关于配置文件的一些问题&#xff1a; 配置文件相对分散。在一个微服务架构下&#xff0c;配置文件会随着微服务的增多变的越来越多&…

8.22 PowerBI系列之DAX函数专题-盈亏平衡分析

需求 实现 一、用参数设置固定成本&#xff0c;单位变动成本&#xff0c;与毛利率 1 单位变动成本 generateseries(0,100,1) 2 固定成本 generateseries(0,50000,1) 3 毛利率 generateseries(0,0.4,0.01) 二、度量值 1 总变动成本 [单位变动成本 值]*[销量 值] 2 总成本…

各类电机数学模型相关公式总结 —— 集成芯片驱动

0、背景技术概述 永磁直流电机&#xff08;PMDC&#xff09;、永磁同步电机&#xff08;PMSM&#xff09;、无刷直流电机&#xff08;BLDC&#xff09;以及混合式两相步进电机在小功率应用场景中多采用集成芯片驱动&#xff08;如二合一、三合一驱动芯片&#xff09;的原因主要…

深度学习之非极大值抑制NMS介绍

1. 基本介绍 非极大值抑制&#xff08;Non-Maximum Suppression&#xff0c;NMS&#xff09;是深度学习中一种常用的目标检测算法&#xff0c;用于在检测结果中去除冗余的边界框。 在目标检测任务中&#xff0c;通常会使用候选框&#xff08;bounding boxes&#xff09;来表示可…

王道408数据结构CH2_线性表

概述 2 线性表 2.1 基本操作 2.2 顺序表示 线性表的元素从1开始&#xff0c;数组元素下标从0开始 2.2.1 结构体定义 #define Maxsize 50typedef struct{ElemType data[Maxsize];int length; }SqList;#define Initsize 100typedef struct{ElemType *data;int Maxsize ,length;…

Ansible部署 之 zookeeper集群

简介 Ansible是近年来越来越火的一款轻量级运维自动化工具&#xff0c;主要功能为帮助运维实现运维工作的自动化、降低手动操作的失误、提升运维工作效率。常用于自动化部署软件、自动化配置、自动化管理&#xff0c;支持playbook编排。配置简单&#xff0c;无需安装客户端&am…

Github 2024-06-06 Go开源项目日报 Top10

根据Github Trendings的统计,今日(2024-06-06统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Go项目10Ollama: 本地大型语言模型设置与运行 创建周期:248 天开发语言:Go协议类型:MIT LicenseStar数量:42421 个Fork数量:2724 次关注人…

js中的事件循环机制(宏任务和微任务)

JavaScript的事件循环机制是其非阻塞I/O模型的核心&#xff0c;它使得JavaScript能够在单线程环境中高效地处理异步操作。事件循环机制主要由以下几个部分组成&#xff1a; 调用栈&#xff08;Call Stack&#xff09;&#xff1a; 这是JavaScript执行同步代码的地方&#xff0c…

Android状态栏适配问题

Android状态栏适配是一个老生常谈的问题&#xff0c;那么我又拿出来讲了&#xff0c;因为这个东西确实太重要了&#xff0c;基本上每个项目都用得到。状态栏总共有几种形态。第一&#xff0c;让状态栏颜色跟应用主色调一致&#xff0c;布局内容不占有状态栏的位置。第二&#x…

c++学习笔记“类和对象”;友元函数

目录 4.4 友元 4.4.1 全局函数做友元 4.4.1 类做友元 4.4.1 成员函数做友元 4.4 友元 生活中你的家有客厅(Public)&#xff0c;有你的卧室(Private) 客厅所有来的客人都可以进去&#xff0c;但是你的卧室是私有的&#xff0c;也就是说只有你能进去但是呢&#xff0c;你也可…

PSOPT在Ubuntu22.04下的安装

求解器pospt的原链接如下&#xff1a; PSOPT/psopt: PSOPT Optimal Control Software (github.com) 在该文件夹下提供了安装的指导文件&#xff0c;文件内容如下&#xff1a; 在 Ubuntu 22.04 中&#xff0c;如果按照适用于 Ubuntu 20.04 的说明执行 PSOPT 代码&#xff0c;目…

详细分析Mysql中的SQL_MODE基本知识(附Demo讲解)

目录 前言1. 基本知识2. Demo讲解2.1 ONLY_FULL_GROUP_BY2.2 STRICT_TRANS_TABLES2.3 NO_ZERO_IN_DATE2.4 NO_ENGINE_SUBSTITUTION2.5 ANSI_QUOTES 前言 了解Mysql内部的机制有助于辅助开发以及形成整体的架构思维 对于基本的命令行以及优化推荐阅读&#xff1a; 数据库中增…