多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[toc] # 几个概念 ## 路径 在一棵树中,从一个结点到另一个结点所经过的所有结点,被我们称为两个结点之间的路径。 比如:A -> H 的路径: ![](https://img.kancloud.cn/1d/d4/1dd44197a23aa5aa130a0e573339e6eb_1236x1084.png) ## 路径长度 在一棵树中,从一个结点到另一个结点所经过的“边”的数量,被我们称为两个结点之间的路径长度。 比如:A -> H的路径长度为 3: ![](https://img.kancloud.cn/34/bc/34bc723d1197eb6f40b5d7ab31ab0819_1216x1076.png) ## 节点的带权路径长度 每一个节点都可以有一个权重(数字)。 带权路径长度:节点权重 * 节点到根节点的路径长度 比如:H 的带权路径长度 = 3 * 3 = 9 ![](https://img.kancloud.cn/25/4d/254d6b2f2a066c713251a34f913be694_1248x1128.png) ## 树的带权路径长度 树的带权路径长度(也做WPL):所有 `叶子` 节点的带权路径长度之和。 比如:下面这棵树的 WPL = 3*3 + 6*3 + 1*2 + 4*2 + 8*2 = 53 ![](https://img.kancloud.cn/57/dc/57dc02737778338ab788f7f00ff1a43d_1220x1148.png) ## 哈夫曼树 哈夫曼树(Huffman Tree):是在叶子结点和权重确定的情况下,带权路径长度最小的二叉树,也被称为最优二叉树。 比如下图左侧就比右侧 WPL 小。 ![](https://img.kancloud.cn/92/ad/92ade9107dccfa537b6f5aac934bd33d_1210x620.png) 总结:核心特点:权重越大的节点,越靠近根节点。 面试官问:什么是最优二叉树?什么是哈夫曼树? ## 哈夫曼编码 哈夫曼编码可以用来进行 `数据压缩` 。 ### 编码 比如要压缩:“acbcbacddaddaddccd” 1. 先统计文本中每个字符出现的频率 ![](https://img.kancloud.cn/e3/ef/e3ef35efcf04208adb20ae55136fc516_502x506.png) 2. 频率当作权重构建哈夫曼树 ![](https://img.kancloud.cn/de/30/de3015365e17db53e820f89cb9364ae1_640x904.png) 3. 根据哈夫曼树对字符串进行编码 编码规则: 1. 左子节点是0,右子节点是1 2. 从根节点到叶子结果 根据哈夫曼树可以将字符串编码为: * a:111 * ac:1111 0 * acb:1111 0110 * acbc:1111 011010 * acbcb:1111 0110 1011 0… 说明:编码之后好像是变长了,但注意这里的存储 单位是不同的: - 1 字节(Byte) = 6 位(bit) - 一个字母 = 1 字节 = 8 位 - 一个汉字 = 2~4字节 = 16~32位 - 而1010这个哈夫曼编码,单位是位,一个1或0只带1 位。 所以综合:哈夫曼编码的压缩率在 20% ~ 90% 之间。 ### 解码 编码过程就是: 1. 将哈夫曼编码到哈夫曼树上 “从根向下跑一遍” 2. 0 向左、 1 向右 3. 当遇到一个 “叶子” 节点时结束 4. 重复从根开始跑,重复 1~3 比如:1111 0110 1011 0... 到树上跑得到:acbcb ## 用途 1. 数据压缩 2. 加密、解密 # 构建哈夫曼树的流程 1. 将带权节点 `按升序` 放到数组中 ![](https://img.kancloud.cn/12/8b/128b5929fcb6921078de7bf385f0a283_240x934.png) 2. 取出数组中最小的两个节点做为子节点构造一棵树,和为父节点 ![](https://img.kancloud.cn/73/d8/73d8ff811a3ba7a711b98cc118321a2a_728x998.png) 3. 将父节点的值放回数组中并 `排序` ![](https://img.kancloud.cn/16/fc/16fce61c87f9ed15056c1bd92e3d6626_698x758.png) 4. 重复2~3步骤 拿出最小两个节点: ![](https://img.kancloud.cn/bd/c0/bdc0ec78d98a96918c1cfe61d4a84af2_800x790.png) 将的父节点放回并排序 ![](https://img.kancloud.cn/fa/bb/fabbcc40b85f411f2c7469373179446d_802x724.png) 5. 重复2~3 ![](https://img.kancloud.cn/19/9e/199ea6adfdb224a03b9021f49f4f76d0_832x810.png) ![](https://img.kancloud.cn/a1/22/a122afea34ac0cb71a3c004df3a51275_802x744.png) 6. 重复2~3 ![](https://img.kancloud.cn/89/ca/89ca511e426fddb9138a888fb724d4ae_958x908.png) ![](https://img.kancloud.cn/da/28/da28935b7f8cff23b6356dbb61e84bc2_1002x938.png) 7. 重复2~3 ![](https://img.kancloud.cn/bd/38/bd38c53f244326d64357c98d280b391e_1220x1138.png) ![](https://img.kancloud.cn/0b/1f/0b1f9c52ef4b2e234cbc8b1c9923800f_1200x1156.png) 最后数组中只剩下一个节点,说明构造结束! # 代码实现 - 构造哈夫曼树 ~~~ class Node { constructor(data) { this.data = data this.left = null this.right = null } } class HuffmanTree { constructor(arr) { this.queue = arr this.root = null let first, second, parent while(this.queue.length>1) { // 排序 this.queue.sort((a,b)=>a-b) first = this.queue.shift() second = this.queue.shift() parent = first + second this.queue.unshift(parent) parent = new Node(parent) first = new Node(first) second = new Node(second) if(this.root === null) { parent.left = first parent.right = second this.root = parent } else { if(this.root.data == first.data) { parent.left = this.root parent.right = second this.root = parent } else { parent.left = first parent.right = this.root this.root = parent } } } } } ~~~