树的存储结构
实现树结构的存储方式
双亲表示法
以双亲作为索引的关键词的一种存储方式
- 以一组连续空间存储树的结点,同时在每个结点中,附设一个指示其双亲结点在数组中位置的元素
所以可以根据某结点的parent指针找到它的双亲结点,所用时间复杂度为O(1),索引到parent=-1,表示找到树结点的根。
如果要知道某结点的孩子,则要遍历整个树。
孩子表示法
多重链表
- 方案一:根据树的度,声明足够空间存放子树指针的结点
浪费空间 - 方案二:数据后面有一个值来表示指针的个数
每个结点的度的值不同,初始化和维护起来难度巨大 - 方案三:根数据后面连接着子结点位置的指针
双亲孩子表示法
孩子后面存储跟结点的指针,同时又有指向子结点的指针
二叉树
定义:二叉树(Binary tree)是n(n>=0)个结点的有限集合,该集合或者为空集(空二叉树),或者由一个根结点和两颗互不相交的,分别为根结点的左子树和右子树的二叉树组成。
性质:
- 每个结点最多有两个子树,所以二叉树中不存在度大于2的结点。
- 左子树和右子树是有顺序的,次序不能颠倒
- 即使树中某结点只有一颗子树,也要区分它是左子树还是右子树
二叉树的五种基本形态
- 空二叉树
- 只有一个根结点
- 根结点只有左子树
- 根结点只有右子树
- 根结点既有左子树尤游右子树
特殊二叉树
斜树(左斜、右斜)
满二叉树
满二叉树所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
- 叶子只能出现在最后一层
- 非叶子的结点的度都是2
- 在同样深度的树中,二叉树的结点是最多的
完全二叉树:
对一颗具有n个结点的二叉树按层序编号,如果编号为i(1<=i<=n)的结点与同样深度的满二叉树中编号为i 的结点位置完全相同,则这颗二叉树称为完全二叉树。
完全二叉树的特点
- 叶子结点只能出现在最下两层
- 最下层的叶子一定集中在左部连续位置
- 倒数第二层,若有叶子结点,一定都在右部连续位置
- 如果结点度为1,则该结点只有左孩子
- 同样结点树的二叉树,完全二叉树的深度最小
- 满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树
二叉树的性质:
- 一:二叉树的第i层上至多有2^(i-1)个结点
- 二:深度为k的二叉树至多有2^k-1个结点(k>=1)
- 三:对任何一颗二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1
- 四:具有n个结点的完全二叉树的深度为[log2 n]+1
- 五:如果对一颗有n个结点的完全二叉树的结点按层序编号,对于任一结点i(1<=i<=n)有以下性质:
如果i=1,则结点时二叉树的根,无双亲;若果i>1,则其双亲是结点[1/2]
如果2i>n,则结点i无左孩子(结点i为叶子结点);否则其左孩子是结点2i
如果2i+1>n,则结点i无右孩子;否则其右孩子是结点2i+1
二叉树的存储结构:
二叉树的顺序存储结构就是用一维数组存储二叉树中的各个结点,并且结点的存储位置能体现结点之间的逻辑关系
对于一般的二叉树,尽管层序编号不能反映逻辑关系,但是也可以按照完全二叉树编号方式修改一下,把不存在的结点用“^”代替
顺序存储方式的适用性不强,二叉树也可以采取链式存储结构
二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域是比较自然的想法,我们称这样的链表叫做二叉链表
lchild | data | rchild |
---|
二叉树的遍历
从根结点出发,按照某种次序依次访问二叉树中所有的结点,使得每个结点被访问一次且仅被访问一次
前序遍历
若二叉树为空,则空操作返回,否则先访问根及诶单,然后前序遍历左子树,再前序遍历右子树
中序遍历
若树为空,则空操作返回,否则从根结点开始(注意不是先访问根结点,先从左子树开始),中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树
后序遍历
若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后访问根结点。
层序遍历
线索二叉树
lchild | ltag | data | rtag | rchild |
---|
ltag为0时指向该结点的左孩子,为1时指向该结点的前驱
rtag为0时指向该结点的右孩子,为1时指向该结点的后继
树、森林及二叉树的相互转换
树到二叉树的转换:
- 第一步:在树中所有兄弟结点之间加一连线
- 第二步:对每个结点,除了保留与其的长子的连线外,去掉该结点与其他孩子的连线
注:兄弟都变成右儿子
森林转化为二叉树
- 第一步:先将森林中的每棵树先变成二叉树
- 第二步:将各二叉树的根结点视为兄弟从左至右连在一起,就形成了一棵二叉树
- 第三步:调整位置
二叉树到树、森林的转换
- 第一步:若结点x是双亲y的左孩子,则把x的右孩子、右孩子的右孩子、……、都与y用连线连起来
- 第二步:去除所有双亲到右孩子之间的连线
判断一棵二叉树能够转换成一棵树还是森林
看这棵二叉树的根结点有没有右孩子,有的话就是森林,没有的话就是一棵树
树的遍历
先根遍历:先访问树的根结点,然后再依次先根遍历每棵子树
后根遍历:先依次遍历每棵子树,然后再访问根结点
森林的遍历
前序遍历和后序遍历,其实就是按照树的先根遍历和后根遍历依次访问森林的每一棵树
注:树、森林的前根(序)遍历和二叉树的前序遍历结果相同,树、森林的后根(序)遍历和二叉树的中序遍历结果相同。
赫夫曼树(哈夫曼树)
定义与原理:把二叉树简化成叶子结点带权的二叉树(注:树结点间的连线相关的数叫做权)
- 结点的路径长度:
从根结点到该结点的路径上的连接数 - 树的路径长度:
树中每个叶子结点的路径长度之和 - 结点带权路径长度:
结点的路径长度与结点权值的乘积 - 树的带权路径长度:
WPL(weighted path length)是树中所有叶子及诶单的带权路径长度之和
WPL的值越小,说明构造出来的二叉树性能越优
构造哈夫曼树(最优二叉树)
权值越小的在远离根的一段
注:赫夫曼编码——无损编码