数据结构——树和森林

文章目录

  • 定义
  • 树的存储结构
  • 树、森林与二叉树相互转换
  • 树的遍历
  • Huffman树
    • 二叉编码树
    • Huffman编码
  • 参考


定义

是由一个集合以及在该集合上定义的一种关系构成的。集合中元素成为树的结点,所定义的关系称为父子关系。当集合为空时,是一棵空树;当集合非空时,此时有且仅有一个特定的称为的结点。
如下图所示,a是一棵空树,b是只有一个根节点的树,c是一棵有10个结点,并且高度为2的树。

数据结构——树和森林_第1张图片

森林是m棵互不相交的树的集合。对树中的每个结点而言,其子树的集合即为森林。删去一棵树的根,就得到一个森林。反之,加上一个结点作为树根,森林就变为一棵树。

树的存储结构

1、双亲表示法:用一个一维数组存储每个结点,数组的下标就是结点的指针位置,每个结点包括一个数据与与指向父亲结点的数组下标的域。
数据结构——树和森林_第2张图片
在这种表示法下,寻找一个结点的父结点只需要O(1)时间。而查询儿子或者兄弟结点的操作可能需要遍历整个数组。
为了节省查询时间,可以规定儿子的数组下标值大于父亲数组下标值,而兄弟结点的数组小标志随着兄弟从左到右递增。如上图所示。


2、孩子链表表示法:用一个线性表来存储树的所有结点信息,称为结点表。每个结点建立一个孩子表,孩子表中只存储孩子结点的地址信息(可以是指针、数组下标或者内存地址)。
数据结构——树和森林_第3张图片
上图中的孩子表是用单链表来实现的。由于每个结点的孩子数目不确定,所以一般不用数组来实现孩子表,但是可以用数组实现结点表。
在孩子链表表示法中,通过某个结点找到其孩子比较容易,只需要遍历孩子链表即可。
然而要找到某个结点的父结点,却需要对每个结点的孩子链表进行遍历,比较麻烦,因此可以在孩子链表表示法的基础上结合双亲表示法,在每个结点域中附设一个指向双亲结点的域。如下图所示。

数据结构——树和森林_第4张图片


3、孩子兄弟表示法:又称二叉树表示法,每个结点除了数据域外,还包含第一个孩子和右邻兄弟。如下图所示,B的第一个孩子是D,B的兄弟结点是C。
数据结构——树和森林_第5张图片


树、森林与二叉树相互转换

通过树的孩子兄弟表示法可以看到,树和二叉树都可以使用二叉链表作为存储结构,因此从二叉链表可以推导出树与二叉树之间的一个对应关系,从实际存储来看,它们二叉链表相同,只是解释不同而已。
数据结构——树和森林_第6张图片
1、树转化二叉树
(1)顺序连接同一结点的兄弟结点;
(2)保留每个结点到其第一个孩子结点的连接作为该结点的左孩子结点,删除这个结点到其他孩子结点的连接;
(3)以树的根结点为中心,顺时针旋转一定角度,使得结构层次分明。
2、森林是若干树的集合,森林转化为二叉树
(1)将森林中的每一刻树转换为相应的二叉树;
(2)将第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树根结点的右孩子,得到二叉树。
数据结构——树和森林_第7张图片
3、二叉树还原为树或森林
按层次序列对二叉树的每个结点做以下操作:
(1)如果是根结点或者是左孩子结点,则不做任何改动;
(2)如果是右孩子,将其当前父亲的父亲设置为当前结点的父结点,若其当前父亲的父亲为空,则改动后其父亲为空。

树的遍历

由树的定义可以得到两种次序遍历树的方法:
(1)先序遍历:访问树的根结点;从左到右,依次先序遍历根的每棵子树。
(2)后序遍历:从左到右,依次后序遍历根的每棵子树;访问树的根结点。
森林的遍历
(1)先序遍历:访问森林第一棵树的根结点;先序遍历第一棵树中根结点的子树树林;先序遍历除去第一棵树后剩余的树构成的森林。上图中森林的先序遍历:ADBEFGCHI
(2)中序遍历:中序遍历第一棵树中根结点的子树树林;访问森林中第一棵树的根结点;中序遍历除去第一棵树后剩余的树构成的森林。上图中森林的中序遍历:DAEFGBHIC
(3)后序遍历:中序遍历第一棵树中根结点的子树树林;中序遍历除去第一棵树后剩余的树构成的森林;访问森林中第一棵树的根结点。上图中森林的后序遍历:DGEFIHCBA

由于树可以看作只有一棵树的森林,所以树的先序遍历和后序遍历分别与森林的先序遍历和中序遍历对应。

而森林的先序。中序、后序遍历与其相对应的二叉树的先序、中序、后序遍历结果相同,因此树的先序和后序遍历可以借助相应二叉树的先序遍历和中序遍历的算法实现。

由先序和中序遍历序列可以唯一确定一棵二叉树;由中序和后序遍历序列可以唯一确定一棵二叉树,但是由先序和后序不能唯一确定一棵二叉树。

例如:先序遍历为:EBADCFHG,中序序列为:ABCDEFGH,可确定二叉树为:
数据结构——树和森林_第8张图片

Huffman树

定义:给定N个权值作为叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的树为哈夫曼树,也称最优树。

数据结构——树和森林_第9张图片
左边树带权路径长度:(2+3+6+9)2=40
右边树带权路径长度:9 + 6
2 +(2+3)*3 = 36

构造哈夫曼树
输入:给定n个权值
构造n棵只有一个根结点的二叉树,n个权值分别是这些二叉树根结点的权,F是由这n棵二叉树构成的集合
重复执行
选取两个最小的实数结点作为二叉树的左右结点,构造新的二叉树,然后根结点为权值之和;
将这两个结点从集合F中删除,加入新构造的树。

数据结构——树和森林_第10张图片

二叉编码树

符号数据在处理之前需要对符号进行二进制编码,例如英文字符的ASCII编码,它使用固定的8为二进制位表示字符,因此是一种定长编码。

为了缩短数据编码长度,可以以采用不定长编码:给使用频度较高的字符编较短的码,这是数据压缩技术的最基本思想。

那么如何给字符进行不定长编码,而使得数据编码的平均长度最短?使用二叉树对字符集中的字符进行编码,方法如下:
(1)将字符集中的所有字符作为二叉树的叶子结点;
(2)每一个“父亲-左孩子”关系对应一位二进制位0,每一个“父亲-右孩子”关系对应一位二进制1;
(3)从根通往每个叶子结点的路径,就对应于相应字符的二进制编码

数据结构——树和森林_第11张图片
A:0,B:100,C:11,D:101
二叉树编码得到的都是前缀编码,前缀编码使得不定长编码在解码过程中不会产生歧义

Huffman编码

对同一个字符集进行编码的二叉树可以有很多,但是哪个编码才是使得编码长度最小的呢?

由于字符集中各种字符出现的概率不同,字符的出现概率决定了编码方案的选择。
其实这将最佳编码方案抽象成为求带权路径长度最小的二叉树,而Huffman树就是这样的树。

参考

数据结构与算法(Java语言版)——周鹏
如有侵权,请联系作者更正

你可能感兴趣的:(数据结构,b树)