Maoyl's blog Maoyl's blog
首页
  • 前端基础

    • HTML
    • CSS
    • CSS动画
    • JavaScript文章
    • stylus
  • 性能优化

    • 《性能优化》笔记
  • 学习笔记

    • 《JavaScript教程》笔记
    • 《JavaScript高级程序设计》笔记
    • 《ES6 教程》笔记
    • 《JavaScript设计模式》笔记
    • 《TypeScript 从零实现 axios》
    • TypeScript笔记
    • JS设计模式总结笔记
  • 前端框架

    • Vue相关
    • React相关
  • 前端监控

    • 前端监控简介
  • 学习笔记

    • 《Vue》笔记
    • 《React》笔记
    • 小程序学习笔记
  • 后端基础

    • Nodejs
  • 学习笔记

    • 数据结构
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • 网页性能
  • 学习笔记

    • 《Git》学习笔记
    • 《Vim》学习笔记
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

maoyln

日日行,不怕千万里
首页
  • 前端基础

    • HTML
    • CSS
    • CSS动画
    • JavaScript文章
    • stylus
  • 性能优化

    • 《性能优化》笔记
  • 学习笔记

    • 《JavaScript教程》笔记
    • 《JavaScript高级程序设计》笔记
    • 《ES6 教程》笔记
    • 《JavaScript设计模式》笔记
    • 《TypeScript 从零实现 axios》
    • TypeScript笔记
    • JS设计模式总结笔记
  • 前端框架

    • Vue相关
    • React相关
  • 前端监控

    • 前端监控简介
  • 学习笔记

    • 《Vue》笔记
    • 《React》笔记
    • 小程序学习笔记
  • 后端基础

    • Nodejs
  • 学习笔记

    • 数据结构
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • 网页性能
  • 学习笔记

    • 《Git》学习笔记
    • 《Vim》学习笔记
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
关于
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 第一讲 基本概念

  • 第二讲 线性结构

  • 第三讲 树(上)

  • 第四讲 树(中)

  • 第五讲 树(下)

    • 堆
    • 哈夫曼树与哈夫曼编码
      • 哈夫曼树与哈夫曼编码的关系
        • 哈夫曼树的定义
        • 哈夫曼编码的定义
        • 哈夫曼树与哈夫曼编码的关系
        • 应用实例
    • 集合及运算
  • 第六讲 图(上)

  • 《数据结构》学习笔记
  • 第五讲 树(下)
maoyln
2024-07-16
目录

哈夫曼树与哈夫曼编码


# 哈夫曼树与哈夫曼编码的关系

哈夫曼树(Huffman Tree)和哈夫曼编码(Huffman Coding)是紧密相关的概念,它们共同构成了一个高效的编码技术,用于无损数据压缩。下面详细阐述它们之间的关系:

# 哈夫曼树的定义

哈夫曼树,又称为最优二叉树或自适应二叉树,是由美国信息理论学家戴维·哈夫曼在1952年提出的。它是一种带权路径长度最短的二叉树,其中“带权”表示树中的每个叶子节点都关联有一个权重,这个权重通常表示某个字符的出现频率。哈夫曼树的路径长度是指从根节点到叶子节点的边的数量,路径长度越短,表明该编码方法更高效。

# 哈夫曼树的特点

  • 通过构建哈夫曼树,可以得到最优的二进制编码方案。
  • 在构建过程中,权重小的字符倾向于靠近树的底部,而权重大的字符则靠近顶部。
  • 哈夫曼编码是一种前缀编码,意味着编码的任何一部分都不可能是另一个编码的开头,避免了编码的混淆。

# 哈夫曼树的构造过程

  1. 计算字符频率:首先,计算出每个字符在数据集中的出现频率。
  2. 构建优先队列:将字符及其频率构成的节点放入优先队列中,优先级由低到高排序。
  3. 合并节点:从优先队列中取出两个最低频率的节点,创建一个新的内部节点,其频率等于这两个节点的频率之和,并将新节点加入队列。
  4. 重复合并:重复上述步骤,直到队列中只剩下一个节点,这个节点就是哈夫曼树的根。
  5. 生成编码:从根节点开始,遍历到每个叶子节点,记录路径上的方向(左或右),将其转换为0或1,即为该字符的哈夫曼编码。
# 示例

假设有以下字符及其频率:

// key表示字符,value表示字符出现的次数
let a = {
  a: 50,
  b: 25,
  c: 12,
  d: 12,
  e: 8,
  f: 8,
  g: 4,
}
1
2
3
4
5
6
7
8
9
10
# 构造哈夫曼树的过程如下:
初始化:创建7个初始节点。
构建优先队列:将节点按频率排序。
合并节点:
合并频率最小的两个节点(e和f)。
再次合并,这次是最低的三个节点(e/f, g, d)。
接着是(e/f/g/d, b)。
最后一次合并是(a, e/f/g/d/b)。
生成编码:从根节点向下,a的编码为0, b为10, c为110, d为1110, e为11110, f为11111, g为111110。
1
2
3
4
5
6
7
8
# javascript实现

function huffmanEncode(frequencies) {
    const nodes = Object.entries(frequencies).map(([char, freq]) => ({ char, freq }));
    const priorityQueue = new MinPriorityQueue({ compare: (a, b) => a.freq - b.freq });
    nodes.forEach(node => priorityQueue.enqueue(node));
    
    while (priorityQueue.size() > 1) {
        const left = priorityQueue.dequeue();
        const right = priorityQueue.dequeue();
        const mergedNode = { freq: left.freq + right.freq, left, right };
        priorityQueue.enqueue(mergedNode);
    }
    
    const tree = priorityQueue.dequeue();
    
    const codes = {};
    function traverse(node, code) {
        if (node.left) {
            traverse(node.left, code + '0');
            traverse(node.right, code + '1');
        } else {
            codes[node.char] = code;
        }
    }
    
    traverse(tree, '');
    
    return codes;
}

const frequencies = {
    a: 50,
    b: 25,
    c: 12,
    d: 12,
    e: 8,
    f: 8,
    g: 4
};

const encoded = huffmanEncode(frequencies);
console.log(encoded);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

这段代码实现了哈夫曼编码的基本逻辑,包括字符频率的计算、构建哈夫曼树以及生成编码的过程。注意,为了简化实现,这里使用了一个假设的 MinPriorityQueue 类,你需要根据所使用的JavaScript库或框架自行实现优先队列。

# 哈夫曼编码的定义

哈夫曼编码是一种前缀编码(Prefix Code),也叫作自适应编码,它是通过对字符进行编码以达到数据压缩的目的。在哈夫曼编码中,每个字符都有一个唯一的二进制编码,而且这个编码不会是另一个编码的前缀,这保证了编码的唯一性。这种编码方式利用了字符的统计特性,对出现频率高的字符给予较短的编码,反之则给予较长的编码,从而达到整体编码效率的提升。

# 哈夫曼树与哈夫曼编码的关系

  • 构造关系:哈夫曼树是哈夫曼编码的基础。通过构造哈夫曼树,我们可以获得一个最优的编码方案。具体而言,哈夫曼树的叶子节点代表数据集中不同的字符,而非叶子节点只作为路径的标记。在构建哈夫曼树时,我们按照字符的频率从小到大合并节点,最终形成的树具有最短的带权路径长度,对应的编码方案即是最优编码。
  • 编码规则:在哈夫曼树中,从根节点到任意叶子节点的路径上,向左走表示编码为0,向右走表示编码为1。因此,叶子节点的编码就是其字符的哈夫曼编码。由于哈夫曼树的构造确保了路径的最短化,所以哈夫曼编码本质上就是哈夫曼树的叶子节点到根节点的路径的反向表示。
  • 解码过程:基于哈夫曼树的结构,可以通过递归地遍历树来解码。给定一段编码,从根节点出发,遇到0则向左子树移动,遇到1则向右子树移动,直到到达叶子节点,该叶子节点上的字符即为被解码的字符。

# 应用实例

  • 文本压缩:在文本文件压缩中,哈夫曼编码常用于压缩英语文本等语言的文件。通过对文件中每个字符的频率进行分析,构造出相应的哈夫曼树,然后用更少的比特数来表示文件内容。
  • 图像压缩:在JPEG和PNG等图像格式中,哈夫曼编码也扮演了重要角色。通过对图像像素的频率分布进行分析,可以构造出针对特定图像的哈夫曼树,从而有效地压缩图像数据。
编辑 (opens new window)
#数据结构
上次更新: 2025/04/18, 01:42:12
堆
集合及运算

← 堆 集合及运算→

最近更新
01
GSAP动画库——如何高效写动画
04-17
02
自适应方案PxToRem
09-10
03
性能优化-requestAnimationFrame
08-10
更多文章>
Theme by Vdoing | Copyright © 2019-2025 备案号:京ICP备19058102号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式