树和森林
树的存储结构
1.双亲表示法
结构数组存放每个结点,每个结点包含数据域+双亲域(双亲在数组中位置)。
找双亲容易,找孩子难。
2.孩子链表
结构数组存放每个结点,每个结点包含数据域+指针域(指向第一个孩子的头指针)。n个结点有n个孩子链表(叶子的孩子链表为空)。
找孩子容易,找双亲难。
附加:带双亲的孩子链表。
增加双亲结点下标,牺牲空间。
3.孩子兄弟表示法(二叉链表表示法)常用
与二叉树不同的是不只有两个孩子、不区分左右结点顺序,两个指针分别指向第一个孩子结点与它的下一个兄弟结点。
左指针可以找到第一个孩子结点,第一个孩子的右指针可以找到另一个结点。
找孩子容易,要找双亲可以再增加一个指针域。
树与二叉树的转换
树与二叉树都可以用二叉链表进行存储,则以二叉链表作为媒介来转换。
存储之后,解释规则不一样,则可得到另一个树。
给定一棵树,可以找到唯一对应的二叉树。
对树的操作可以转换成对二叉树的操作。因为实际上都是对结点数据进行操作,转换树结构没关系。
1.树转换二叉树:树二叉链表中的兄弟结点在二叉树中变右孩子。
兄弟相连留长子
2.二叉树转换树:二叉树二叉链表中的右孩子在树中变兄弟结点。
左孩右右连双亲,去掉原来右孩线。
森林与二叉树的转换
森林定义:m棵互不相交的树 m>=0
1.森林转换成二叉树:先都变成二叉树再连。
树变二叉根相连
2.二叉树转换成森林:先解散成二叉树再变成树。
去掉全部右孩线,孤立二叉再还原。
树与森林的遍历
树的遍历
森林的遍历
也可按照对树的遍历方式对森林中每棵树进行遍历。
哈弗曼树
基本概念
找到最优的判断树(最优二叉树),就是哈弗曼树。
1.路径:树中一个结点到另一个结点的分支。
2.路径长度:两结点路径上的分支数。
3.树的路径长度:从树根到每个结点的路径长度之和。
[ 结点数相同的二叉树,完全二叉树是最短路径长度的二叉树(充分条件)]
4.结点的权weight:给结点赋予具有某种意义的值。
5.结点的带权路径长度:从根结点到该结点之间路径长度与该结点权的乘积。
6.树的带权路径长度:树中所有叶子结点的带权路径长度之和。
带权路径长度最短的二叉树 = = 哈弗曼树
构造哈弗曼树的算法 = = 哈夫曼算法
权值越大的叶子离根越近,哈弗曼树不唯一。
构造算法
贪心算法:构造哈弗曼树时首先选择权值小的叶子结点。
如何构造哈夫曼树?
即哈夫曼算法:不断选择两个权值小的结点构造成树。
构造算法的实现
采用顺序存储结构——一维结构数组
结点类型定义://数组大小为2n,不用0即可
实现算法的思路:
1.创建2n长度数组0–2n-1,将1–2n-1都初始化为0。
2.给n个叶子结点赋予权值,寻找最小叶子结点合并成新结点,新结点依次存入数组的后n位中。
3.赋予叶子结点的双亲结点,并从挑选最小结点的表中删除掉叶子结点。
4.赋新结点的权值为左右孩子的和,赋左右孩子。
哈弗曼编码思想
等长编码方式缺点:浪费空间
不定长二进制编码:待传字符串中出现次数多的使用较短编码,节省空间。
设计不定长二进制编码时要避免在翻译阶段产生重码问题。
哈弗曼编码定义:
1.将字符作为叶子结点构造哈弗曼树,概率值即为叶子结点权值。
2.哈弗曼树左分支标记0,右分支标记1,从根结点到叶子结点路过的编码就是叶子结点的编码。//从上往下路过的在码中从左到右写
哈弗曼编码性质:
哈弗曼编码的算法实现
关注如何得到叶子结点编码。
由构造的哈弗曼树来得到叶子结点编码,从叶子结点开始,不断往上找双亲结点,若当前结点为左孩子,则记0,右孩子记1。得到的编码再反过来即可。
文件的编码和解码
文件的哈弗曼编码:
文件的哈弗曼解码: