(六)树
树的定义
(1)n > 0 时,根结点是唯一的。不可能有多个根结点;
(2)m > 0 时,子树的个数没有限制,但它们一定是互不相交的。每个结点有且只有一个父结点(向上的边只有一条)
结点分类:根结点、内部结点、叶结点(度为0的结点)
树的一些基本术语
树的存储结构
树的存储结构:双亲表示法、孩子表示法、孩子兄弟表示法
双亲表示法:
假设以一组连续空间存储树的结点,同时在每个结点中,附设一个指示器指示其双亲结点到链表中的位置。即每个结点除了知道自己是谁,还知道它的双亲在什么位置。
| data|parent |
data是数据域,存储结点的数据信息;parent是指针域,存储该结点的双亲在数组中的下标。根结点的位置域设置为-1;
孩子表示法:
每个结点有多个指针域,其中每一个指针指向一颗子树的根结点,多重链表表示法。
方案(1):指针域个数等于树的度。
data Child1 Child2 ……child n
data是数据域,child是指针域,指向该结点的孩子结点。
指针域的个数是3个。但是树中结点的度相差很大时,显然在浪费空间。
方案(2):每个结点指针域的个数等于该结点的度。
data degree Child1 ……child n
Degree是度域,也就是存放该结点的孩子结点的个数。
孩子兄弟表示法:任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。设置两个指针,分别指向该结点的第一个孩子和此结点的右兄弟。
data Firstchild Firstsib
二叉树
二叉树定义:二叉树是n(n>0)个结点的有限集合,该集合或者空集或者是由一个根结点和两棵互不相交的,分别称为根结点的左子树和右子树的二叉树组成。
二叉树的特点:
(1) 每颗结点最多有两棵子树,所以二叉树中不存在度大于2的结点。没有子树或者一棵子树也可以;
(2) 左子树和右子树是有顺序的,次序不能颠倒;
(3) 即使树中某结点只有一棵子树,也是要区分它是左子树还是右子树。
二叉树的五种形态:
特殊二叉树:
斜树有个明显的特点,就是每层都只有一个结点,结点个数与二叉树的深度(层数)相同。
满二叉树
在一棵二叉树中,所有分支结点都存在左子树和右子树,并且所有的叶子都在同一层上,这样的二叉树称为满二叉树。
满二叉树的特点:
(1) 叶子结点只能出现在最下一层;
(2) 非叶子结点的度一定是2;
(3) 同样深度的二叉树中,满二叉树的结点个数是最多的,叶子数是最多的;
完全二叉树:
(1) 满二叉树一定是完全二叉树,但是完全二叉树不一定是满二叉树;
(2) 同样深度下,完全二叉树与满二叉树结点编号是完全一样的;
完全二叉树的判断:
给每个结点按照满二叉树的结构逐层顺序编号,如果编号出现空档,就不是完全二叉树。
二叉树的性质
二叉树的存储结构
二叉树的存储结构——顺序存储结构
二叉树的存储结构——二叉链表:
二叉树每个结点最多有两个孩子,所以设计了一个数据域和两个指针域,称这样的链表叫做二叉链表。
遍历二叉树
二叉树的遍历是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。
遍历方法:
**先序遍历根——左——右
中序遍历
左——根——右
后序遍历
左——右——根
先序、中序和后序遍历过程:遍历过程中经过的结点路线一样,只是访问各结点的时机不同。
线索二叉树
线索二叉树的原理:
在二叉链表上,只能知道每个结点指向其左右孩子结点的地址,但是并不知道某个结点的前驱是谁,后继是谁,想要知道,就要遍历一遍。但是如果在创建之前就知道前驱和后继,会节省很多时间。
把指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表。相应的二叉树称为线索二叉树。
线索二叉树等于就是把一棵二叉树转变成了一个双向链表。
将空指针域中的rchild,改为指向它的后继结点(中序遍历)
将空指针域中的lchild,改为指向当前结点的前驱结点(中序遍历)
但是,如何判断结点是指向前驱还是指向左孩子?需要一个区分标志
树、森林、二叉树的转换
(1)树转换成二叉树的步骤
(2)森林转换为二叉树
森林是由若干棵树组成,森林中的每一棵树都是兄弟,可以按照兄弟的处理方法进行操作;
哈夫曼树及其应用
哈夫曼树定义与原理:
从树中一个结点到另一个结点之间的分支构成了两个结点之间的路径,路径上的分支数目称作路径长度。
树的路径长度就是从树根到每一个结点的路径长度之和。考虑到带权重的结点,结点的带权路径长度为从该节点到树根之间的路径长度与结点上权的乘积。树的带权路径长度就是树中所有叶子结点的带权路径长度之和。
带权路径长度WPL最小的二叉树称为哈夫曼树。
哈夫曼树构造:
每次把权值最小的两棵二叉树合并。
哈夫曼树的特点:
哈夫曼编码:
不等长的编码会存在二义性:
避免编码过程中的二义性:
用前缀码(prefix code)避免二义性
前缀码:任何字符的编码都不是另一字符编码的前缀。可以无二义地解码。
保证编码不会出现二义性——可以用二叉树进行编码:
将所有字符都放在二叉树的叶结点上,可以无二义解码。
答:用哈夫曼树进行构造;每次把权值最小的两棵二叉树合并。
优点 :代价最小 不会有二义性;