![1b32d9cef78960e03cfc41bfb0c0aaeb.png](https://i-blog.csdnimg.cn/blog_migrate/6cc7f683ae0e5936e7696a2fa4056156.png)
基础知识
感谢某位在后台留言的同学,让我想起来我还有这个没写完的系列。
在最开始,先了解几个基础概念:
- 路径:在一棵树中,一个结点到另一个结点之间的通路,称为路径。
![9553746dc887dd45bce3f926493c1f82.png](https://i-blog.csdnimg.cn/blog_migrate/c84f21466831a159b5cf8d3e3087109a.png)
上面这个二叉树中,根节点 A 到叶子结点 I 的路径,就是A,B,D,I。
- 路径长度:在一条路径中,每经过一个结点,路径长度都要加 1 。例如在一棵树中,规定根结点所在层数为1层,那么从根结点到第 i 层结点的路径长度为 i - 1 。
![5c981279e047ae3553e05f44844ec78a.png](https://i-blog.csdnimg.cn/blog_migrate/9a10d85922605f9502ed5c27e29ba751.png)
在这个二叉树中,根节点 A 到叶子结点 H 的路径长度就是 3 。
- 结点的权:给每一个结点赋予一个数值,被称为这个结点的权。
- 结点的带权路径长度:指的是从根结点到该结点之间的路径长度与该结点的权的乘积。
![f7bb39bc8087d2cfde1659eaf585d96f.png](https://i-blog.csdnimg.cn/blog_migrate/a55fc7ef6585e26907811fdbf47dcf5a.png)
我们假设节点 H 的权是 5 ,从根结点到结点 H 的路径长度是 3 ,那么结点 H 的带权路径长度是 3 X 5 = 15。
- 树的带权路径长度:在一棵树中,所有叶子结点的带权路径长度之和,被称为树的带权路径长度,也被简称为 「WPL」 。
![6be137e480d29cf0bbd691961a4211cf.png](https://i-blog.csdnimg.cn/blog_migrate/08ca4b5dc21eb4d812397876c704f14a.png)
还是这颗树,它的带权路径长度是 1 X 2 + 2 X 1 + 2 X 3 + 2 X 4 + 3 X 5 + 3 X 6 = 51 。
哈夫曼树
哈弗曼树就是在用 n 个结点(都做叶子结点且都有各自的权值)试图构建一棵树时,如果构建的这棵树的带权路径长度最小,称这棵树为「最优二叉树」,有时也叫「哈夫曼树」或者「赫夫曼树」。
在构建哈弗曼树时,要使树的带权路径长度最小,只需要遵循一个原则,那就是:权重越大的结点离树根越近。
需要注意的是,同样叶子结点所构成的哈夫曼树可能不止一颗,在同一层,左右叶子节点交换位置,树的带权路径长度是一样的,就比如下面这两个哈夫曼树:
![4144d20ee73d3a61ab08d10bd8b2b1cc.png](https://i-blog.csdnimg.cn/blog_migrate/ccd0fc57c8735a90bde9c596b2655b90.png)
哈弗曼树的构建过程
那么如何构建一个哈夫曼树呢?我们这里举个例子,比如我们有这么几个叶子节点:3,4,7,9,13,15,17:
![0636c094018f79920f819b3114487445.png](https://i-blog.csdnimg.cn/blog_migrate/336ee79738f1cdddad82d90569825c14.png)
第一步:选出两个最小的权值,对应的两个结点组成一个新的二叉树,且新二叉树的根结点的权值为左右孩子权值的和:
![7a69ac6727b4b3200a8a6481c2f59c24.png](https://i-blog.csdnimg.cn/blog_migrate/73181ad5813000b983989331bb2d22e9.png)
第二步:从队列中移除上一步选择的两个最小结点,把新的父节点加入队列,也就是从队列中删除 3 和 4 ,插入 7 ,并且仍然保持队列的升序:
![3446d86defb5c459bca54bfadf4c0ad5.png](https://i-blog.csdnimg.cn/blog_migrate/145ff66a1c7b5f142d94e3bbd517b555.png)
第三步:选择当前权值最小的两个结点,生成新的父结点。
这一步其实是在重复上一步操作,当前队列中最小的节点是 7 和 7 ,生成新的父结点权值是 7 + 7 = 14 :
![29153ea1e10bc8c79a40fbc656546e77.png](https://i-blog.csdnimg.cn/blog_migrate/951a589ef48618236b82a927179592b3.png)
第四步:从队列中移除上一步选择的两个最小结点,把新的父节点加入队列。
这一步依然是在重复,从队列中删除 7 和 7 ,加入 14 ,并且仍然保持队列的升序:
![61ec53e63222bd1dc6567b35855ee34d.png](https://i-blog.csdnimg.cn/blog_migrate/c43c2e97b1c096937a9f413986fecdbc.png)
第五步:选择当前权值最小的两个结点,生成新的父结点。
这一步还是重复操作。当前队列中权值最小的结点是 9 和 14 ,生成新的父结点权值是 9 + 14 = 23 :
![ba7a382596577c79684078da5aa58534.png](https://i-blog.csdnimg.cn/blog_migrate/31bb6619b8adf9a113758d8d85503536.png)
第六步:从队列中移除上一步选择的两个最小结点,把新的父节点加入队列。
这一步依然是在重复,从队列中删除 9 和 14 ,加入 23 ,并且仍然保持队列的升序:
![5982c638eba3f1e2691cd9edcfe59ff2.png](https://i-blog.csdnimg.cn/blog_migrate/18d7fbed489f01667cf869e44470dc31.png)
第七步:选择当前权值最小的两个结点,生成新的父结点。
这一步从队列中选择权值最小的结点是 13 和 15 ,生成新的父结点权值是 13 + 15 = 28 :
![94b061e00e37246a4c07fb41e1660364.png](https://i-blog.csdnimg.cn/blog_migrate/17d8e8599ea9a404a5316cf5a9a7593f.png)
第八步:从队列中移除上一步选择的两个最小结点,把新的父节点加入队列。
从队列中删除 13 和 15 ,加入 28 ,并且仍然保持队列的升序:
![2199b9e19c51f4f2b24c8533a6d71fc8.png](https://i-blog.csdnimg.cn/blog_migrate/f583d99a19247840628d2f5146111240.png)
第九步:选择当前权值最小的两个结点,生成新的父结点。
这一步从队列中选择权值最小的结点是 17 和 23 ,生成新的父结点权值是 17 + 23 = 40 :
![daf4b961cd2c303090fa6882fe783175.png](https://i-blog.csdnimg.cn/blog_migrate/23c734283f12459c615f50fce3666c04.png)
第十步:从队列中移除上一步选择的两个最小结点,把新的父节点加入队列。
从队列中删除 17 和 23 ,加入 40 ,并且仍然保持队列的升序:
![92607706193879658e6b97b874587e61.png](https://i-blog.csdnimg.cn/blog_migrate/e04d76a5d71edf924e8031d96956b810.png)
第十一步:选择当前权值最小的两个结点,生成新的父结点,移除队列中的最后两个节点(懒得画了,最后两步并一步)。
这一步从队列中选择权值最小的结点是 28 和 40 ,生成新的父结点权值是 28 + 40 = 68 :
![4e70ad58c479da071a4384967e8c1682.png](https://i-blog.csdnimg.cn/blog_migrate/56d9cbd63fbb681d9e2869afeccfd026.png)
此时,我们得到的这棵树就是哈弗曼树。
哈夫曼树就介绍到这里,下一节,将会介绍哈夫曼树的用途:哈夫曼编码。
参考
http://c.biancheng.net/view/3398.html
https://baijiahao.baidu.com/s?id=1663514710675419737&wfr=spider&for=pc
感谢阅读![4bf76253f00ea563fe692b61d963006d.png](https://i-blog.csdnimg.cn/blog_migrate/87bd52f8ed6e6f8ea766690f07b10403.png)