具体内容:
该文章的源代码仓库为:
上一节介绍了二叉树,这一节简要介绍一下树。我们知道二叉树,每个节点至多有2个孩子,而普通的树则没有了这种限制,每个节点可以有多个孩子。
一、树中的相关定义
- 1.度:节点拥有的孩子个数是节点的度,树的度为树中节点度的最大值。
- 2.高度:树的最大层次为树的高度或深度。
- 3.森林:m(m≥0)可互不相交的树的集合。
二、树的存储结构
树有三种存储结构:双亲表示法、孩子表示法、孩子兄弟表示法
1.双亲表示法
用线性表存储树,每个节点必须保存其双亲在数组中的下标,也可为了访问方便保存其第一个孩子的下标、右兄弟下标等。如下图所示(来自《大话数据结构》)的树,
每个节点保存双亲下标和右兄弟下标,用双亲表示法表示为:
- 优点: 数据结构简单,访问双亲方便,如果存储了第一个孩子节点或右兄弟等信息,则访问这些信息也会比较方便。
- 缺点: 获取所有的孩子很困难,且如果每个节点存储类似第一个孩子节点或右兄弟等信息,很浪费存储空间(这些信息属于冗余信息)。且这种结构,遍历树很不方便。
2.孩子表示法
用链式结构存储树,即每个节点有多个指针域,每个指针指向一颗子树的根节点。但由于每个节点的孩子不同,指针的个数就不同,对此,解决办法是,把每个结点的孩子排列起来,以单链表形式存储,则n个节点有n个孩子链表,如果是叶子结点则此单链表为空。然后n个头指针又组成一个线性表,采用顺序存储结构,存在一个一维数组中,上面的树用孩子表示法如下所示:
但此时,找节点的双亲仍然不方便,故可以在每个头节点中,存储其双亲信息。如下所示:
- 优点: 在头结点中增加双亲定义后,查找双亲、孩子都很方便
- 缺点: 查找兄弟节点不方便
3.孩子兄弟表示法
任意一棵树,它的节点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此,每个节点设置两个指针,分别指向该节点的第一个孩子和该节点的右兄弟。对于最上面的那棵树,用此类方法表示为:
**此种方式最大的好处是将树变成了一颗二叉树。**将上图中的节点位置整理一下,得到下图:
- 优点: 将树转换成一颗二叉树,可以对其应用二叉树的一些特性,而且这种方式可以很方便地获取节点的孩子(先找到节点的第一个孩子节点a,再找a的兄弟b,再找b的兄弟c,如此不断重复),也可以很方便地获取节点的兄弟。
- 缺点: 查找双亲不方便,但这个可以通过二叉树的一些其他结构来弥补(如将二叉树线索化)。
三、赫夫曼树
1.定义
用n个结点构建一棵树时,如果构建的这棵树的带权路径长度和最小,那称这棵树为“最优二叉树”,也叫赫夫曼树。其特点是:权重越大的结点距根节点越近。
2.赫夫曼树的构建
赫夫曼树的构建过程为:
- (1)有n个节点组成的集合s,从集合s中,取出权重最小的两个节点,将其作为一个二叉树的左子树和右子树,其根节点的权值为两个节点的和。
- (2)将(1)中生成的根节点放入到s中,重复执行(1)过程,直到s中的元素全部取出。
整个过程如下图所示(图片来源于此):
3.赫夫曼树代码
我这里基于二叉树的二叉树的实现,来构建二叉树。这里只用Java实现,C++类似,我这里就不再实现了。
因为赫夫曼树中经常需要涉及到节点的比较,所以我们首先定义一个基础数据类型,该基础数据类型中包含该数据的id和权重,且包含一个比较函数,用于比较两个数据的大小。
- 数据基类
public class HuffBaseType{
protected String id;//节点ID
protected int weight;//权重
public HuffBaseType