在介绍哈夫曼树之前需要先了解一些专业术语

  • 路径和路径长度

  在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。

  • 结点的权及带权路径长度

  若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。

  • 树的带权路径长度

  树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL =(W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,...n)。

1、什么是哈夫曼树

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

2、哈夫曼树的构造规则

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

  1. 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
  2. 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
  3. 从森林中删除选取的两棵树,并将新树加入森林;
  4. 重复2、3步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。

3、哈夫曼编码

哈夫曼树的目的是为了解决当年远距离通信(主要是电报)的数据传输的最优化问题

比如,我们需要在网络上传输「BADCADEEFD」字符串序列给其他人,显然用二进制的数字(0和1)来表示是很自然的想法。每个字符占一个字节,如果要压缩的话可以通过二进制编码的方式进行传输,这个字符串包含了6个字符:ABCDEF,我们可以用对应的二进制表示如下:

这样,真正传输的数字编码就是「001000011010000011100100101011」,对方接收时按照3位一分来译码,如果文章很长,这个序列串也会非常长。

而事实上,不管是英文还是中文,不同字母或汉字的出现频率是不同的,完全可以用哈夫曼树的思想进行优化。

上述「BADCADEEFD」中不同字符的出现大致概率是这样的:

合起来是100%,我们可以这样来构建哈夫曼树:

再将左分支改为0,右分支改为1,对应的哈夫曼树如下:

此时,我们对这6个字母用其从树根到叶子所经过路径的 0或1 来编码,可以得到如下图的定义

我们将文字内容为「BADCADEEFD」再次编码,对比可以看到结果串变小了

原编码二进制串:001000011010000011100100101011(30个字符)
新编码二进制串:1001101101110100100100001(25个字符)

也就是说,我们的数据被压缩了,节约了大约 17%的存储或传输成本 随着字符的增加和多字符权重的不同,这种压缩会更加显出其优势。

哈夫曼编码的结果会导致不同字符编码长短不一,很容易混淆。因此在解码时,还是要用到哈夫曼编码,即发送方和接收方必须要约定好同样的哈夫曼编码规则。

4、PHP代码实现

 <?php
/**
* HNode.php
* Created on 2019/5/4 13:11
* Created by Wilin
*/ class HNode
{
public $data;
public $weight; public $code = '';
public $left = null;
public $right = null; public function __construct($data, $weight = 1) {
$this->data = $data;
$this->weight = $weight;
}
}
 <?php
/**
* HuffmanTree.php
* Created on 2019/5/4 13:11
* Created by Wilin
*/
include "HNode.php"; class HuffmanTree
{
private $wpl = 0;
private $root;
private $nodes; public function getTree() {
return $this->root;
} public function getWpl() {
return $this->wpl;
} private function sortByWeight() {
usort($this->nodes, function ($nodeA, $nodeB) {
return $nodeA->weight <=> $nodeB->weight;
});
} public function create($text) { $text = str_replace(' ','',strtolower($text)); for ($i = 0; $i < mb_strlen($text); $i++) {
$index = $data = $text[$i];
if (empty($this->nodes[$index])) {
$newNode = new HNode($data);
$this->nodes[$index] = $newNode;
} else {
$this->nodes[$index]->weight++;
}
} while (sizeof($this->nodes) > 1) {
$this->sortByWeight();
$min1 = array_shift($this->nodes);
$min2 = array_shift($this->nodes); $newNode = new HNode(null, $min1->weight + $min2->weight);
$newNode->left = $min1;
$newNode->right = $min2;
array_push($this->nodes, $newNode);
} $this->root = array_shift($this->nodes);
$this->fillCW($this->root);
} private function fillCW($tree, $code = '') {
if($tree == null) {
return;
}
$tree->code = $code;
if(!$tree->left && !$tree->right){
$this->wpl += mb_strlen($tree->code)*$tree->weight;
}
$this->fillCW($tree->left, $code.'0');
$this->fillCW($tree->right, $code.'1');
} private function preOrderTraverse($tree) {
if($tree == null) {
return;
}
if(!$tree->left && !$tree->right){
printf("%s : %s\n", $tree->data, $tree->code);
}
$this->preOrderTraverse($tree->left);
$this->preOrderTraverse($tree->right);
} public function traverse() {
$this->preOrderTraverse($this->root);
}
} $HTree = new HuffmanTree();
$HTree->create('BADCADEEFD');
$HTree->traverse();
printf("WPL : %s\n", $HTree->getWpl());
print_r($HTree->getTree());

打印结果如下:

E:\www\tree\4>php HuffmanTree.php
e : 00
b : 010
c : 011
d : 10
f : 110
a : 111
WPL : 25
HNode Object
(
[data] =>
[weight] => 10
[code] =>
[left] => HNode Object
(
[data] =>
[weight] => 4
[code] => 0
[left] => HNode Object
(
[data] => e
[weight] => 2
[code] => 00
[left] =>
[right] =>
) [right] => HNode Object
(
[data] =>
[weight] => 2
[code] => 01
[left] => HNode Object
(
[data] => b
[weight] => 1
[code] => 010
[left] =>
[right] =>
) [right] => HNode Object
(
[data] => c
[weight] => 1
[code] => 011
[left] =>
[right] =>
) ) ) [right] => HNode Object
(
[data] =>
[weight] => 6
[code] => 1
[left] => HNode Object
(
[data] => d
[weight] => 3
[code] => 10
[left] =>
[right] =>
) [right] => HNode Object
(
[data] =>
[weight] => 3
[code] => 11
[left] => HNode Object
(
[data] => f
[weight] => 1
[code] => 110
[left] =>
[right] =>
) [right] => HNode Object
(
[data] => a
[weight] => 2
[code] => 111
[left] =>
[right] =>
) ) ) )

哈夫曼树详解——PHP代码实现的更多相关文章

  1. 数据结构图文解析之:哈夫曼树与哈夫曼编码详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  2. 数据结构图文解析之:AVL树详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  3. 赫夫曼树JAVA实现及分析

    一,介绍 1)构造赫夫曼树的算法是一个贪心算法,贪心的地方在于:总是选取当前频率(权值)最低的两个结点来进行合并,构造新结点. 2)使用最小堆来选取频率最小的节点,有助于提高算法效率,因为要选频率最低 ...

  4. 哈夫曼树(三)之 Java详解

    前面分别通过C和C++实现了哈夫曼树,本章给出哈夫曼树的java版本. 目录 1. 哈夫曼树的介绍 2. 哈夫曼树的图文解析 3. 哈夫曼树的基本操作 4. 哈夫曼树的完整源码 转载请注明出处:htt ...

  5. 哈夫曼树(二)之 C++详解

    上一章介绍了哈夫曼树的基本概念,并通过C语言实现了哈夫曼树.本章是哈夫曼树的C++实现. 目录 1. 哈夫曼树的介绍 2. 哈夫曼树的图文解析 3. 哈夫曼树的基本操作 4. 哈夫曼树的完整源码 转载 ...

  6. 哈夫曼树(一)之 C语言详解

    本章介绍哈夫曼树.和以往一样,本文会先对哈夫曼树的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现:实现的语言虽不同,但是原理如出一辙,选择其中之一进行了解即可.若 ...

  7. 哈夫曼树C++实现详解

    哈夫曼树的介绍 Huffman Tree,中文名是哈夫曼树或霍夫曼树,它是最优二叉树. 定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若树的带权路径长度达到最小,则这棵树被称为哈夫曼树. 这个定 ...

  8. 高级数据结构---赫(哈)夫曼树及java代码实现

    我们经常会用到文件压缩,压缩之后文件会变小,便于传输,使用的时候又将其解压出来.为什么压缩之后会变小,而且压缩和解压也不会出错.赫夫曼编码和赫夫曼树了解一下. 赫夫曼树: 它是一种的叶子结点带有权重的 ...

  9. java实现哈夫曼树进行文件加解压

    目录 1.哈夫曼树简述 2.构造树的节点 3.构造哈夫曼树的类(压缩) 4.构造哈夫曼树的类(解压) 5.整体工程文件(包括测试类) 6.结果 7.参考链接 1.哈夫曼树简述 给定n个权值作为n个叶子 ...

随机推荐

  1. Kotlin数据类型 Unit、Nothing与Nothing?、Any与Any?

    Kotlin数据类型 Unit.Nothing与Nothing?.Any与Any?   本文链接:https://blog.csdn.net/ldxlz224/article/details/9440 ...

  2. matlab学习笔记2--matlab的帮助

    一起来学matlab-matlab学习笔记2--matlab的帮助 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考书籍 <matlab 程序设计与综合应用>张德丰等著 感谢张 ...

  3. Pythonrandom模块(获取随机数)常用方法和使用例子

    Python random模块(获取随机数)常用方法和使用例子 这篇文章主要介绍了Python random模块(获取随机数)常用方法和使用例子,需要的朋友可以参考下 random.random ra ...

  4. 关于IO的操作(文件、网络)

    IO操作的流程总结和分析: (1)对象,易于编写代码    --->   (2)byte[],底层本质   ---->  (3)IO(文件.网络),最终IO处理掉

  5. jvm(5)---垃圾回收(回收算法和垃圾收集器)

    1.垃圾回收算法 1.1 标记-清除算法 算法分为“标记”和“清除”阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象.它是最基础的收集算法,效率也很高,但是会带来两个明显的问题 ...

  6. 最新 安易迅java校招面经 (含整理过的面试题大全)

    从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿. 安易迅等10家互联网公司的校招Offer,因为某些自身原因最终选择了 安易迅.6.7月主要是做系统复习.项目复盘.Leet ...

  7. 最新 博盾习言java校招面经 (含整理过的面试题大全)

    从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿. 博盾习言等10家互联网公司的校招Offer,因为某些自身原因最终选择了 博盾习言.6.7月主要是做系统复习.项目复盘.Le ...

  8. Appium移动自动化测试-----(十)appium API 之上下文操作

    其实上下文的操作主要针对于混合应用.啥是混合应用,简单来说就是APP用里面嵌入网页.Android上的浏览器就属于混合应用. 1.获取当前上下文 方法: getContext() 获取当前所有的可用的 ...

  9. 特征抽取: sklearn.feature_extraction.FeatureHasher

    sklearn.feature_extraction.FeatureHasher(n_features=1048576, input_type="dict", dtype=< ...

  10. __init__调用之二

    class Bar: def __init__(self,name,age): self.suibian = name #self后的名字是啥,对象就可以调用啥,而不是 __init__ 后括号形参 ...