普通树与二叉树的相互转化及哈夫曼树的了解

普通树与二叉树的相互转化及哈夫曼树的了解

二叉树与普通树的转化

二叉树的种种特性使得它更便于处理,如果能将普通树转化成二叉树就好了。

普通树 -> 二叉树

回忆孩子兄弟表示法,有第一孩子域(左孩子),还有左孩子的兄弟域。孩子表示法表示的树很容易转化成二叉树,所以只需把树先转为孩子兄弟表示法的样子,再调整层次结构就可以得到二叉树。

  • 加线。在所有兄弟结点之间加一条线。
  • 去线。只保留父结点与第一个孩子(左孩子)的连线,与其他孩子的连线删掉。
  • 调整层次,结点的左孩子依然为左孩子,左孩子的兄弟全变成了结点右孩子。

toBi.PNG

森林也可以转化成二叉树,所谓森林就是树的集合

  • 把森林的每一棵树转成二叉树
  • 以某一棵树作为起始树,下一棵树的根结点作为右孩子连接到上一课树的根结点。直到处理完最后一棵树。

senlintoBi.PNG

二叉树 -> 普通树

  • 加线。如果某个结点存在左孩子,则将左孩子的右结点,其右结点的右孩子...也就是一直深入到没有右孩子,将这些结点与父结点连线。
  • 去线。删除所有结点与其右孩子的连线。
  • 调整结构,让原本某结点的右孩子与该结点处于一个水平线,则他们成为了兄弟。

bito.PNG

二叉树也能变成森林。

从根结点开始,如果存在右孩子,断开与右孩子的连线。接着处理上分离后的二叉树的根结点,如果存在右孩子,断开连线....如此反复,直到某结点无右孩子。然后将得到的若干二叉树转为普通树。

bitosenlin.PNG

哈夫曼树与哈夫曼编码

哈夫曼编码用在数据压缩领域。我们先来看哈夫曼树。

哈夫曼树的构造

树中一个结点到另一个结点之间的分支构成了路径,路径上分支的数目称为路径长度。树的路径长度就是:根结点到每一个结点的路径长度之和。再把一棵二叉树的叶子结点带上权值,定义结点的带权路径长度为:根结点到叶子结点的路径长度 * 叶子结点的权值。那么树的带权路径长度为所有叶子结点的带权路径长度之和。

比如有结点数为n的二叉树,有m个叶子结点。权值分别是w1, w2, w3...根结点到它们的路径长度分别是m1, m2, m3...则m1*w1 + m2*w2 + m3*w3 +...+ mm*wm就是这棵树的带权路径长度。

haffuman_haffuman.PNG

比如二叉树a,结点A的路径长度为1,结点D的路径长度为4...于是该树的带权路径长度就是:5*1 + 15*2 +40*3 +30*4 + 10*4 = 315

二叉树b的带权路径长度为:40*2 + 5*3 + 15*3 +30*2 + 10*2 = 220

由于n个结点的二叉树有多种可能的形态,叶子结点的个数、结点的路径长度都各不相同,这些树的带权路径长度有的很大有的很小。我们的目的是要找出使树的带权路径长度最小的二叉树,这样的二叉树称为哈夫曼树。哈夫曼树的形态也不唯一(比如某棵哈夫曼树作镜面对称),但是这些树带权路径长度一定是唯一值。

haffuman_haffuman2.PNG

上图就是一棵哈夫曼树,它的带权路径长度为40*1 + 30*2 + 15*3 + 5*4 + 10*4 = 205,比前面两种情况的值都小。那么这棵树是怎么来的呢?幸好有简单的构造方法。

  • 先将带权的叶子结点按照权值从小到大排序,以上图为例子就是{A: 5, E: 10, B: 15, D: 30, C: 40}
  • 取出前两个结点,新增一个N1结点作为这两个结点的父结点,权值小的作为N1的左孩子,权值稍大的作为右孩子,并将N1的权值为设置为这两个结点的权值之和,如图N1的权值为A和E的权值之和15。
  • 已取出的叶子结点从序列中删除,并将新增的N1结点插入到序列中合适的位置,保持序列有序。然后重复上一步骤。直到所有叶子的结点的都已被取出,至此就完成了哈夫曼树的构造。(完成的图就是上图那样)

haffuman_3.PNG

哈夫曼编码

试想将一段字符通过网络传输给别人,如BADCADFEED,由于其中只有ABCDEF六个字母,用三位的二进制数就可以完全表示。每个字母被编码成以下表格所示。

字母ABCDEF
二进制编码000001010011100101

这样上面的BADCADFEED编码后就是001000011010000011101100100011,长度是30。对方接收到这一长串再根据上表,每三位代表一个字母,解码出真正的序列。

现在我们尝试用哈夫曼编码对这段数据进行压缩。假如我们有一段字符要进行传输,这段字符中共出现6个字母,每个字母出现的频率是{A: 27, B: 8, C: 15, D: 15, E: 30, F: 5}。根据上面介绍的哈夫曼树的构造,可以得到下面的左图。

4.PNG

现在将结点到左孩子的路径权值改为0,到右孩子的权值改为1,从根结点到叶子结点所经过的路径组成的0、1序列就是该字符的编码。举比如字符D被编码为00,可以列出每个字符的编码序列。

字母ABCDEF
二进制编码01100110100111000

可以看出二进制位数参差不齐了,频率高的二进制位数少,频率低的所需位数就多。BADCADFEED现在被编码成了1001010010101001000111100,长度是25。比上面少了5个字符,这说明我们可以用更少的数据量传输内容,却不丢失语意。我们确实成功压缩了数据。

对方接收到这一长串,再根据上表解码出真正的序列。不过在解码的时候,由于表中的编码各个字符的二进制位数不是固定,有的3位数、有的4位数....如果某个编码序列是另外一个编码序列的前缀,在解码的时候我们就不能确定这到底是哪个字符。所以在编码的时候一定要避免这样的情况发生,编码方案需要满足:任意字符的编码都不是其他任意字符编码的前缀,这种编码称为前缀编码。


by @sunhaiyu

2017.9.14

转载于:https://www.cnblogs.com/sun-haiyu/p/7521466.html

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

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

相关文章

node 将汉字转化为拼音

使用npm包:pinyin,这还是一个两年前发布的神库吧。 1、安装 yarn add pinyin2、使用 const pinyin require("pinyin");console.log(pinyin("中心")); // [ [ zhōng ], [ xīn ] ] console.log(pinyin("中心", {he…

oracle 10 数据库覆盖

同事经常发来一个DMP文件,要求覆盖数据库,我一般用下面的方法完成: 首先删除该用户,再新建用户,导入数据到该用户。 1、drop user username cascade; 2、新建用户、付权 3、导入数据 imp 今天导数据时遇到一个…

android 百分数与进度显示

double percent entity.getFundraisingMoney() / entity.getInvestmentProjectAll(); //输出一下,确认你的小数无误 System.out.println("小数:" percent); //获取格式化对象 NumberFormat nt NumberFormat.getPercentInstance(); //设置百…

【基础】ORACLE中on commit preserve rows和 on commit delete rows的区别

首先on commit preserve rows 和 on commit delete rows 都是在oracle 创建临时表时用到的, delete rows用于事务相关,也就在事务结束后truncate data in the temporary table.preserve rows表示在会话结束后清除临时表的数据前者在事务提交后数据就已经清除了&…

跳出内层循环 使用 for of 代替 map

有些场景,比如表单验证的时候,只要有一个字段没有填写,就给出toast提示,这就需要一发现问题,就给出提示,并且跳出循环。 map想要直接跳出循环,需要使用抛出异常的写法,而for of则适…

oracle 10 expdp impdp 导入、导出

今天收到到一个数据库的包(.dup),要求导入到现有的数据库中,平时我们收到的包(.dmp)尾缀不一样,按正常的方法无法导入, 报错:IMP-00010 :不是有效…

android Module之间数据传递

方法一:使用接口回调 (1)在子module创建回调接口(参数可变) public interface OnChangeLisener {void onChanged(Date date);}(2)在子module 实现类设置接口回调 //设置选择回调 public void …

node 根据图片img url 获取 base64

自己都觉得搞笑的是,之前写前端图片裁剪代码的时候,想解决的问题是如何将canvas裁剪的base64图片转化为file格式上传。而现在考虑的问题是,如何将网络中的图片转化为base64图片格式。 两种写法,思想一摸一样。一种是http库实现的&…

130242014037-汤毓聪-实验一

实验报告 课程 软件体系结构与设计 实验名称 软件设计的网络环境 第 页 专业 软件工程 班级 1班 学号 1302420140 姓名 实验日期: 2017 年 9 月 14 日 报告退发 (订正 、 重做) 一、实验目的 1.复习软件工程的重要概念,熟悉…

软件测试管理之困惑

软件测试管理 最近研究技术的时间少一些,一直在看关于软件测试管理之类的文档,然后整理公司的一些流程。 公司和大多数中国的软件公司一样,有许多的地方不规范,毕竟咱也不是外包,没有规范的流程与管理,呵…

android ViewPager 图片浏览和保存图片

在build.gradle在添加依赖 compile com.alibaba:fastjson:1.1.54.android compile org.ligboy.retrofit2:converter-fastjson-android:2.1.0compile com.bm.photoview:library:1.4.1 compile xiaofei.library:android-data-storage:1.3.0 在layout下创建activity_photo_browser…

mongodb mongoose 常用操作符号 整理

操作符描述$eq等于$or或关系$nor或关系取反$gt大于$gte大于等于$lt小于$lte小于等于$ne不等于$in在多个值范围内$nin不在多个值范围内$all匹配数组中多个值$regex正则,用于模糊查询$size匹配数组大小$maxDistance范围查询,距离(基于LBS&#…

oracle 数据库新建实例导入数据

oracle 数据库中如何新建实例,然后导入数据 在工作经常需要建一个库,建个表空间,再新建用户,配置权限,导入数据。 第一步 用DBCA新建数据库,这个就下一步好&…

android java判断字符串是否为空和是否是手机号和是否是数字,数字转中文

数字转中文 private String getD(int d) { // String[] str { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };String[] str {"零",…

UUID工具类及使用

1.工具类: package UUIdtest;import java.util.UUID;public class UUIDUtil {public static String getUUID() {return UUID.randomUUID().toString();}} 2.使用 package UUIdtest;import org.junit.Test;public class TestUUID {Testpublic void test3(){System.out.println(U…

常用的富文本编辑器推荐

1、tinymce 2、quill https://quilljs.com/