哈夫曼编码和哈夫曼树

news2025/7/16 10:36:40

哈夫曼编码(Huffman Coding) 是一种基于字符出现频率的无损数据压缩算法,通过构建哈夫曼树(Huffman Tree) 来生成最优前缀编码,使得高频字符用短编码,低频字符用长编码,从而实现高效压缩。


1. 哈夫曼树(Huffman Tree)

  • 定义:哈夫曼树是一棵带权路径长度(WPL)最小的二叉树。
    • 权值:字符的出现频率(或概率)。
    • 带权路径长度:每个叶子节点的权值 × 其到根节点的路径长度之和。
  • 特点
    • 没有度为1的节点(严格的二叉树)。
    • 频率越高的字符离根越近,编码越短。
构建哈夫曼树的步骤
  1. 统计字符频率:为每个字符赋予权值(频率)。
  2. 创建节点集合:每个字符作为一个叶子节点,组成初始森林。
  3. 合并最小权值节点
    • 每次选择权值最小的两个节点,合并成一个新父节点,权值为两者之和。
    • 重复合并,直到只剩一棵树。
  4. 生成编码:从根到叶子的路径,左分支为 0,右分支为 1(或相反)。

示例
假设字符 A(5), B(9), C(12), D(13), E(16), F(45)
构建过程如下:

  1. 初始节点集合:5, 9, 12, 13, 16, 45
  2. 合并最小的 59 → 新节点 14
  3. 合并 1213 → 新节点 25
  4. 合并 1416 → 新节点 30
  5. 合并 2530 → 新节点 55
  6. 合并 4555 → 根节点 100
    最终哈夫曼树结构如下:
         (100)
       /       \
    F(45)     (55)
            /      \
         (25)      (30)
        /    \     /   \
    C(12)   D(13)(14)  E(16)
                 / \
              A(5) B(9)

2. 哈夫曼编码(Huffman Coding)

  • 规则:从根到叶子节点的路径生成二进制编码。
    • 左分支为 0,右分支为 1(或相反)。
  • 示例(基于上述哈夫曼树):
    • F(45)0
    • C(12)100
    • D(13)101
    • A(5)1100
    • B(9)1101
    • E(16)111
编码特点
  1. 前缀码(Prefix Code):任何字符的编码都不是其他编码的前缀,避免解码歧义。
  2. 最优性:在所有前缀码中,哈夫曼编码的压缩效率最高(WPL最小)。

3. 哈夫曼编码的压缩与解压

压缩过程
  1. 统计字符频率,构建哈夫曼树。
  2. 生成字符到二进制编码的映射表。
  3. 将原始数据替换为哈夫曼编码的二进制流。
解压过程
  1. 根据哈夫曼树或编码表,从二进制流中逐位解码。
  2. 从根节点开始,根据 0/1 向左/右子树移动,直到到达叶子节点,输出对应字符。

4. 代码实现(C++ 示例)

#include <iostream>
#include <queue>
#include <unordered_map>
using namespace std;

// 哈夫曼树节点
struct Node {
    char ch;
    int freq;
    Node *left, *right;
    Node(char c, int f) : ch(c), freq(f), left(nullptr), right(nullptr) {}
};

// 优先队列的比较器(按频率升序)
struct Compare {
    bool operator()(Node* a, Node* b) {
        return a->freq > b->freq;
    }
};

// 构建哈夫曼树
Node* buildHuffmanTree(const unordered_map<char, int>& freqMap) {
    priority_queue<Node*, vector<Node*>, Compare> pq;
    for (auto& pair : freqMap) {
        pq.push(new Node(pair.first, pair.second));
    }
    while (pq.size() > 1) {
        Node* left = pq.top(); pq.pop();
        Node* right = pq.top(); pq.pop();
        Node* parent = new Node('\0', left->freq + right->freq);
        parent->left = left;
        parent->right = right;
        pq.push(parent);
    }
    return pq.top();
}

// 生成编码表
void generateCodes(Node* root, string code, unordered_map<char, string>& codes) {
    if (!root) return;
    if (root->ch != '\0') {
        codes[root->ch] = code;
        return;
    }
    generateCodes(root->left, code + "0", codes);
    generateCodes(root->right, code + "1", codes);
}

int main() {
    unordered_map<char, int> freqMap = {{'A',5}, {'B',9}, {'C',12}, {'D',13}, {'E',16}, {'F',45}};
    Node* root = buildHuffmanTree(freqMap);
    unordered_map<char, string> codes;
    generateCodes(root, "", codes);
    for (auto& pair : codes) {
        cout << pair.first << ": " << pair.second << endl;
    }
    return 0;
}

5. 应用场景

  • 文件压缩:如 ZIP、GZIP、JPEG、MP3 等格式。
  • 数据传输:减少带宽占用。
  • 数据库存储:压缩文本字段。

6. 优缺点

  • 优点
    • 无损压缩,解压后数据完全恢复。
    • 对高频字符优化,压缩效率高。
  • 缺点
    • 需要存储哈夫曼树或编码表,可能增加额外开销。
    • 不适合字符频率分布均匀的场景。

总结

哈夫曼编码通过构建最优二叉树实现高效压缩,是经典的数据压缩算法之一。理解其核心思想(贪心算法)和实现步骤(构建树、生成编码),是掌握数据压缩技术的基础。


练一练

在这里插入图片描述
答案:B

在这里插入图片描述
答案:A

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

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

相关文章

中西面点实训室虚拟仿真操作平台

在餐饮行业蓬勃发展的当下&#xff0c;中西面点作为其中极具特色与市场需求的重要分支&#xff0c;对于专业人才的渴望愈发强烈。一个功能完备、设施先进的中西面点实训室&#xff0c;已然成为培养高素质面点专业人才的关键阵地。凯禾瑞华——实训室建设 一、中西面点实训室建设…

C++游戏服务器开发之⑦redis的使用

目录 1.当前进度 2.守护进程 3.进程监控 4.玩家姓名添加文件 5.文件删除玩家姓名 6.redis安装 7.redis存取命令 8.redis链表存取 9.redis程序结构 10.hiredisAPI使用 11.基于redis查找玩家姓名 12.MAKEFILE编写 13.游戏业务实现总结 1.当前进度 2.守护进程 3.进程监…

模拟投资大师思维:AI对冲基金开源项目详解

这里写目录标题 引言项目概述核心功能详解多样化的AI投资智能体灵活的运行模式透明的决策过程 安装和使用教程环境要求安装步骤基本使用方法运行对冲基金模式运行回测模式 应用场景和实际价值教育和研究价值潜在的商业应用与现有解决方案的对比局限性与发展方向 结论 引言 随着…

Cocos Creater打包安卓App添加隐私弹窗详细步骤+常见问题处理

最终演示效果,包含所有代码内容 + 常见错误问题处理 点击服务协议、隐私政策,跳转到相关网页, 点击同意进入游戏,不同意关闭应用 一,添加Activity,命名为MyLaunchActivity 二,编写MyLaunchActivity.java的内容 package com.cocos.game.launch;import android.os.Bund…

Android 热点二维码简单示例

Android 热点二维码简单示例 一、前言 Android 原生设置有热点二维码分享功能&#xff0c;有些系统应用也会有这个需求。 下面看看是如何实现的。 本文是一个比较简单的内容。 二、热点二维码生成实现 1、效果 整个应用就一个普通的Activity&#xff0c;显示一个按钮和二维…

JAVAEE(网络原理—UDP报头结构)

我们本篇文章要讲的是UDP的报头结构以及注意事项。 下面呢&#xff0c;我先说一下UDP是什么&#xff1f; 1.UDP是什么&#xff1f; UDP是一种网络协议。网络协议是计算机网络中&#xff0c;为了使不同设备之间能够准确、高效地进行数据交换和通信&#xff0c;而预先制定的一…

通过docker create与export来分析诊断故障镜像

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

LINUX419 更换仓库(没换成)find命令

NAT模式下虚拟机需与网卡处在同一个网段中吗 和VM1同个网段 会不会影响 这个很重要 是2 改成点2 倒是Ping通了 为啥ping百度 ping到别的地方 4399 倒是ping通了 准备下载httpd包 下不下来 正在替换为新版本仓库 报错 failure: repodata/repomd.xml from local: [Er…

鸿蒙学习笔记(5)-HTTP请求数据

一、Http请求数据 http模块是鸿蒙内置的一个模块&#xff0c;提供了网络请求的能力。不需要再写比较原始的AJAS代码。 ps:在项目中如果要访问网络资源&#xff0c;不管是图片文件还是网络请求&#xff0c;必须给项目开放权限。 &#xff08;1&#xff09;网络连接方式 HTTP数…

Spark-SQL核心编程

Spark-SQL核心编程 数据加载与保存 加载数据 spark.read.load 是加载数据的通用方法。如果读取不同格式的数据&#xff0c;可以对不同的数据格式进行设定 保存数据 df.write.save 是保存数据的通用方法。如果保存不同格式的数据&#xff0c;可以对不同的数据格式进行设定 …

LVGL源码(9):学会控件的使用(自定义弹窗)

LVGL版本&#xff1a;8.3 LVGL的控件各式各样&#xff0c;每种控件都有自己的一些特性&#xff0c;当我们想要使用一个LVGL控件时&#xff0c;我们首先可以通过官网去了解控件的一些基本特性&#xff0c;官网链接如下&#xff1a; LVGL Basics — LVGL documentation&#xf…

8、表单控制:预言水晶球——React 19 复杂表单处理

一、水晶球的预言本质 "每个表单都是时空裂缝中的预言容器&#xff0c;"占卜课教授特里劳妮凝视着水晶球&#xff0c;"React-Hook-Form与Formik的融合&#xff0c;让数据捕获如同捕捉未来碎片&#xff01;" ——以魔法部神秘事务司的预言厅为隐喻&#xf…

8 编程笔记全攻略:Markdown 语法精讲、Typora 编辑器全指南(含安装激活、基础配置、快捷键详解、使用技巧)

1 妙笔在手&#xff0c;编程无忧&#xff01; 1.1 编程为啥要做笔记&#xff1f;这答案绝了&#xff01; 嘿&#xff0c;各位键盘魔法师&#xff01;学编程不记笔记&#xff0c;就像吃火锅不配冰可乐 —— 爽到一半直接噎住&#xff01;你以为自己脑子是顶配 SSD&#xff0c;结…

Linux(autoDL云服务器)mamba-ssm环境安装——一次成功!

1.创建环境选择torch2.0&#xff0c; cuda11.8&#xff0c;python3.8 2.从GitHub官网下载cp38对应的&#xff0c;causl_conv1d&#xff0c;和mamba-ssm2.2.2。下载入下图所示。 3.直接用finalshell 或者xshell连接服务器上传&#xff0c;到根目录下面。 直接用pip install *…

代码审计入门 原生态sql注入篇

前置知识&#xff1a; 漏洞形成的原因&#xff1a; 1、可控的参数 2、函数缺陷 代码审计的步骤&#xff1a; 1、全局使用正则搜索 漏洞函数 &#xff0c;然后根据函数看变量是否可控&#xff0c;再看函数是否有过滤 2、根据web的功能点寻找函数&#xff0c;然后根据函数看…

spring Ai---向量知识库(一)

在一些垂直领域以及公司内部信息相关或者实时性相关的大模型应用&#xff0c;就无法直接使用chatGPT。 这个时候&#xff0c;向量知识库就进入了。 通过坐标向量最接近的即为匹配相关答案。 向量模型定义&#xff1a;将文档向量化&#xff0c;保证内容越相似的文本&#xff0c;…

jmeter利用csv进行参数化和自动断言

1.测试数据 csv测试数据如下&#xff08;以注册接口为例&#xff09; 2.jemer参数化csv设置 打开 jmeter&#xff0c;添加好线程组、HTTP信息头管理器、CSV 数据文件设置、注册请求、响应断言、查看结果树 1&#xff09; CSV 数据文件设置 若 CSV 中数据包含中文&#xff0c;…

数据结构实验7.2:二叉树的基本运算

文章目录 一&#xff0c;实验目的二&#xff0c;问题描述三&#xff0c;基本要求四&#xff0c;实验操作五&#xff0c;示例代码六&#xff0c;运行效果 一&#xff0c;实验目的 深入理解树与二叉树的基本概念&#xff0c;包括节点、度、层次、深度等&#xff0c;清晰区分二叉…

Go-zero框架修改模版进行handler统一响应封装

使用go-zero快速生成接口的时候&#xff0c;发现还是有一些情况不太好处理&#xff0c;比如说&#xff0c;想要自定义响应封装等等。 最开始第一版写api文件的时候&#xff0c;写法是这样的。 type LoginRequest {UserName string json:"userName"Password string …

AI专题(一)----NLP2SQL探索以及解决方案

前面写了很多编码、算法、底层计算机原理等相关的技术专题&#xff0c;由于工作方向调整的缘故&#xff0c;今天开始切入AI人工智能相关介绍。本来按照规划&#xff0c;应该先从大模型的原理开始介绍会比较合适&#xff0c;但是计划赶不上变化&#xff0c;前面通用大模型的工作…