树型结构 是一类重要的非线性数据结构
,其中以树和二叉树最为常用,直观来看,树是以分支关系定义的层次结构。树型结构在客观世界中广泛存在,比如人类社会中的祖辈关系,社会机构组织等等都可以用树来形象表示。树型结构在计算机领域中也得到了广泛应用。
树(Tree) 是n(n>=0)n(n>=0) n ( n >=0)个结点的有限集,在任意一棵非空树中满足:
树的结构定义是一个递归的定义,即在树的定义中又用到树的概念, 递归是树固有的特性 。在树型结构中,除了根结点以外,任何一个结点有且仅有一个前驱,每个结点可以有0个或多个后继。结点数为0的树又称之为空树。
树的结点包含一个数据元素及若干指向其子树的分支。结点拥有的子树数称为结点的 度(Degree) **,**以上图为例,结点D的度为3,因为结点D有三条分支结构;依次列举,结点C的度为1,结点B的度为2等等。
度为0的结点称为叶子(Leaf)结点或 终端结点 ,以上图为例,结点K、L、F、G、M、I、J都是度为0的结点,所以他们也被称为叶子结点或终端结点。
度不为0的结点称为非终端结点或 分支结点 。除根节点外,分支结点也称为内部结点,以上图为例,结点B、C、D、E、H的度都不为零,所以他们称为非终端结点或分支结点。
树的度是树内各结点的度的最大值,以上图为例,在整棵树中,结点中度的最大值是3,所以树的度就为3。
结点的子树的根称为该结点的 孩子(Child) ,相应地,该结点称为孩子的 双亲(Parent) ,以上图为例,结点B的孩子结点为E和F,反之,结点B也是结点E、F的双亲结点。
同一个双亲的孩子之间互称 兄弟(Sibling), 以上图为例,结点H、I、J的双亲为同一个结点D,所以结点H、I、J互为兄弟结点。
结点的祖先是从根到该结点所经分支上的所有结点,反之,以某结点为根的子树中的任一结点都称为该结点的 子孙 。以上图为例,结点G的祖先就为结点A和C,反之,结点C的子孙就为结点G。
结点的层次(Level) 是从根开始定义的,根为第一层,根的孩子为第二层(自上往下数)
以上图为例,结点G的层次为3。
树中结点的最大层次称为树的深度(Depth) 或高度(其实就是整棵树总共有多少层)
以上图为例,树的深度或高度为4。
结点数 = 总度数 + 1 以上图为例,结点A的度数为3;结点B的度数为2;结点C的度数为1,结点D的度数为3;结点E的度数为2;结点H的度数为1;其余结点K、L、F、G、M、I、J的度数为0,所以这棵树的结点数就等于:
T结点数 = 3+2+1+3+2+1+1;最后结果为13
森林(Forest) 是m(m>=0) m(m>=0)m(m>=0)棵互不相交的树的集合,对树中的每个结点而言,其子树的集合即为森林。
森林可以为空
二叉树(Binary Tree) 是另一种树型结构,它的特点是每个结点至多只有两棵子树(即二叉树中不存在度大于2的结点
),并且,二叉树的子树有左右之分, 其次序不能任意颠倒 。
二叉树或为空,或是由一个根结点加上两棵分别称为左子树和右子树的、互不相交的二叉树构成,由于这两棵子树也是二叉树,则由二叉树的定义,它们也可以是空树。由此, 二叉树可以有5种基本形态 。
二叉树具有下列重要特性
一棵深度为 kkk且有2k-1个结点的二叉树称为 满二叉树 ,这种树的特点是每一层上的结点数都是最大结点数,只有最后一层是叶子结点且不存在度为1的结点。
深度为 kkk的,有 nnn 个结点的二叉树,当且仅当其每一个结点都与深度为 kkk的满二叉树中编号从1至nnn的结点一一对应时,称之为 完全二叉树 ,在一棵完全二叉树中只有最后两层可能为叶子结点,且最多只有一个度为1的结点。
完全二叉树的三个重要特性:
左子树上所有结点的关键字均小于根结点的关键字,右子树上所有结点的关键字均大于根结点的关键字,左子树和右子树又各是一棵二叉排序树。
二叉排序树用于元素的搜索和排序:
按照 顺序存储结构的定义 ,用一组地址连续的存储单元依次自上而下、从左往右
存储完全二叉树的结点元素,即将完全二叉树上编号为i ii的结点元素存储在如上定义的一维数组中下标为i−1 i-1 i−1的分量中。
//二叉树的顺序存储表示
#define MaxSize 100 //二叉树的最大结点数
struct TreeNode
{
ElemType value; //结点中的数据元素
bool isEmpty; //结点是否为空
};
//定义一个长度为MaxSize的数组t,按照从上而下,从左至右的顺序依次存储二叉树中的各个结点
TreeNode t[MaxSize];
对于一般二叉树,则应将其每个结点与完全二叉树上的结点相对照,存储在一维数组中的对应分量中。顺序存储结构更多仅适用于完全二叉树的存储。
由二叉树的定义得知, 二叉树的结点由一个数据元素和分别指向其左、右子树的两个分支构成 ,则表示二叉树的链表中的结点至少包含三个域: 数据域 和 左、右指针域 。有时,为了便于找到结点的双亲,则还可以在结点结构中增加一个指向其双亲结点的指针域。 利用这两种结点结构所得的二叉树的存储结构分别称之为 二叉链表 和三叉链表。
//二叉树的二叉链表存储表示
typedefstruct BiTNode
{
TElemType data; //数据域
struct BiTNode *lchild, *rchild //左右孩子指针
}BiTNode,*BiTree;
在二叉树的一些应用中,常常要求在树中查找具有某种特征的结点,或者对树中全部结点逐一进行某种处理。回顾二叉树的递归定义可知,二叉树是由三个基本单元组成: 根节点、左子树和右子树 ,因此,二叉树的遍历分为先(根)序遍历、中(根)序遍历、后(根)序遍历和层次遍历。
若二叉树为空,则空操作,否则:
若二叉树为空,则空操作,否则:
若二叉树为空,则空操作,否则:
在遍历二叉树时,要想找到某个结点的前驱和后继的信息,得运用二叉树已知的遍历方法从头开始遍历,然后在遍历的过程中动态获取其结点的前驱和后继的信息,那有没有一种方法是已知该结点就已经知道其前驱和后继的信息了呢?答案肯定是有的,为了解决二叉树遍历时所存在的一些问题,线索二叉树也孕育而生。那什么是 线索二叉树 呢?
简单理解就是利用空链域来存放结点的前驱和后继的信息,方便寻找前驱结点和后继结点,提高遍历效率
。因为n个结点的二叉树,有n+1 n+1 n+1个空链域,所以可以利用这些空链域来记录前驱,后继的信息。若结点有左子树,则其lchild lchildlchild域指示其左孩子,否则令lchild lchildlchild域指示其前驱;若结点有右子树,则其rchild rchildrchild域指示其右孩子,否则令rchild rchildrchild域指示其后继。为了避免混淆,尚需改变结点结构,增加两个标志域。
以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做 线索链表 。其中指向结点前驱和后继的指针,叫做 线索 。加上线索的二叉树又称之为 线索二叉树(Threaded Binary Tree) 。对二叉树以某种次序遍历使其变为线索二叉树的过程叫做 线索化 。
在线索树上进行遍历,只要先找到序列中的第一个结点,然后依次找结点后继直至其后继为空时而止。
每个结点中保存指向 “双亲” 的指针;
优点:查找指定结点的双亲很方便;
缺点:查找指定结点的孩子只能从头遍历。
//树的双亲表示法存储表示
#define MAX_TREE_SIZE 100
typedefstruct PTNode{//结点结构
TElemType data;
int parent; //双亲位置域
}PTNode;
typedefstruct{//树结构
PTNode nodes[MAX_TREE_SIZE];
int r,n; //根的位置和结点数
}PTree;
//树的孩子表示法链表存储表示
typedefstruct CTNode{//孩子结点
int child;
struct CTNode *next;
}*ChildPtr;
typedefstruct{
TElemType data;
ChildPtr firstchild; //孩子链表头指针
}CTBox;
typedefstruct{
CTBox nodes[MAX_TREE_SIZE];
int n,r; //结点数和根的位置
}CTree;
优点:可以利用二叉树操作来处理树
//树的孩子兄弟表示法链表存储表示
typedefstruct CSNode{//孩子结点
ElemType data;
struct CSNode *firstchild, *nextsibling;
}CSNode,*CSTree;
树的 先根遍历 :若树非空、先访问根结点,再依次对每棵子树进行先根遍历;
树的 后根遍历 :若树非空、先依次对每棵子树进行后根遍历,最后再访问根结点。
树的 层次遍历 (用队列实现):
(1)先序遍历森林
若森林非空,则可按照下述规则遍历:
(2)中序遍历森林
若森林非空,则可按照下述规则遍历:
哈夫曼(Huffman)树 ,又称为 最优树 ,是一类带权路径长度最短的树
。
假设有nnn个权值( W1,W2,⋅⋅⋅⋅⋅⋅⋅,Wm) (W1,W2,·······,Wm)(W1,W2,⋅⋅⋅⋅⋅⋅⋅,Wm),试构造一棵有n个叶子结点的二叉树,每个叶子结点带权值为WiWiWi,则其中带权路径长度WPL最小
的二叉树称为最优二叉树或哈夫曼树。
从树中的一个结点到另一个结点之间的分支构成这两个结点之间的路径,路径上的分支数目称之为 路径长度 。 树的路径长度 是从树根到每一个结点的路径长度之和。
结点的带权路径长度为该结点到树根之间的路径长度与结点上权值的乘积, 树的带权路径长度 为树中所有叶子结点的带权路径长度之和,通常记作WPL。
树的带权路径长度
根据给定的nnn个权值( W1,W2,⋅⋅⋅⋅⋅⋅,Wn)(W1,W2,······,Wn)(W1,W2,⋅⋅⋅⋅⋅⋅,Wn)构成的n棵二叉树的集合F = (T1,T2,⋅⋅⋅⋅⋅⋅,Tn)F=(T1,T2,······,Tn)F=(T1,T2,⋅⋅⋅⋅⋅⋅,T**n),其中每棵二叉树Ti中只有一个带权为WiWiWi的根结点,其左右子树均为空。
在F中选取两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为其左、右子树上根结点的权值之和。
在F中删除这两棵树,同时将新得到的二叉树加入FFF中。
重复上述2和3,直到FFF只喊一棵树为止,这棵树便是哈夫曼树。
对于任意一棵二叉树来说,把二叉树上的所有分支都进行编号,将所有左分支都标记为0,所有右分支都标记为1。(左0右1)`那么对于树上的任何一个结点,都可以根据从根结点到该结点的路径唯一确定一个编号。对于哈夫曼树上的叶子结点,根据从根结点到该叶子结点的路径所确定的一个编号,就是该叶子结点的哈夫曼编码。
本节文章到此结束,在数据结构与算法中,树与二叉树这一章是比较重要且晦涩难懂的一章,本篇文章知识点较多,有些写的也不是很全面。希望大家在阅读的时候能多看看图,多理解,结合图像表达来理解理论知识。其重点是二叉树的所有相关知识,在实际开发中,二叉树的应用较为广泛,希望我的文章对大家学习数据结构能有所帮助,希望大家都能有所收获。
全部0条评论
快来发表一下你的评论吧 !