文章目录
- 4.1 树的基本概念
-
- 4.1.1 树(Tree)的概念
- 4.1.2 相关术语
- 4.2 二叉树(Binary True, BTree)
-
- 4.2.1 概念:
- 4.2.2 二叉树的性质(p97)
- 4.3 二叉树的存储结构
-
- 4.3.1 二叉树的顺序存储结构
- 4.3.2 二叉树的链式存储结构
- 4.4 二叉树的遍历
-
- 4.4.1 二叉树遍历的递归实现
- 4.4.2二叉树的层次遍历
- 4.4.4 应用举例(p110)
- 4.5 树和森林
4.1 树的基本概念
树形结构中一个结点可以有一个或多个直接后继结点.因此树形结构可以表示更复杂的数据.
4.1.1 树(Tree)的概念
树是N(N>=0)个结点的有限集合, 一棵树满足如下两个条件:
- 当N =0 时, 称为空树
- 当N>0 时, 有且仅有一个称为根的结点, 除根结点外, 其余结点分为m个互不相交的非空集合T1, T2…Tm, 这些集合中每一个都是一棵树, 称为根子树.
森林(Forest)是M(M>=0)棵互不相交的树的集合.树的每个结点的子树是森林, 删除一个非空树的根结点, 它的子树便构成森林.
4.1.2 相关术语
- 结点的度: 树上任一结点所拥有的子树的数目称为该结点的度,
- 叶子, 度为0的结点称为叶子或终端结点
- 树的度: 一棵树中所有结点的度的最大值称为该树的度.
- 结点的层次: 从根开始算起, 根的层次为1, 其余结点的层次为其双亲的层次加1.
- 树的高度: 一棵树中所有结点层次数的最大值为该树的高度或深度.
- 有序树: 若树中各结点的子树从左到右是次序的, 不能互换, 称为有序树, 有序树最左边子树的根称为第1个孩子
- 无序树: 若树中各结点的子树是无次序的,可以互换, 则称为无序树.
- 树的基本运算:
- 求根: Root(T), 求树的根结点
- 求双亲: Parent(T, X), 求结点X在树T上的双亲结点; 若X不在T上, 则结果为一特殊标志;
- 求孩子: Child(T, X, i) 求树T上结点X的第i个孩子; 若X不在T上或X没有第i个孩子, 则结果为一特殊标志;
- 建树: Create(X, T1, …, Tk), K>1: 建立一个以X为根, 以T1, …, Tk为第1, …, k棵子树的树;
- 剪枝: Delete(T, X, i) : 删除T上X结点的第i个子树, 若T无第i个子根, 则为空操作.
- 遍历: TraverseTree(T): 遍历树, 即访问树中每个结点, 且每个结点仅被访问一次.
4.2 二叉树(Binary True, BTree)
4.2.1 概念:
二叉树:是n(n>=0)个元素的有限集合, 该集合或者为空, 或者由一个根及两棵互不相交的左子树或右子树组成 , 其中左子树和右子树也均为二叉树.
基本运算:
- 初始化: Initiate(BT), 建立一个空二叉树,
- 求双亲: Parent(BT, X): 求出二叉树上结点X的双亲结点, 若X是BT的根,或X不是BT上的结点, 运算结果为NULL.
- 求左孩子: Lchild(BT, X)和求右孩子Rchild(BT, X): 分别求出二叉树BT上结点X的左孩子或右孩子, 若X为BT的叶子或X不在BT上, 则返回NULL
- 建立二叉树: Creadte(BT): 建立一个二叉树
- 先序遍历: PreOrder(BT): 按先序对二叉树BT进行遍历, 每个结点被访问一次且仅被访问一次, 若BT为空, 则运算为空操作
- 中序遍历: InOrder(BT): 按中序对二叉树BT进行遍历
- 后序遍历: PostOrder(BT): 按后序对二叉树BT进行遍历
- 层次遍历: LevelOrder(BT): 按层从上往上, 同一层中结点按从左往右的顺序, 对二叉树进行遍历, 每个结点被访问一次且仅被访问一次, 若BT为空, 则运算为空操作.
4.2.2 二叉树的性质(p97)
- 性质1: 二叉树的第i层(i>=1)层上至多有2i-1个结点.
- 性质2: 深度为k(k>=1)的二叉树至多有2k-1个结点.
- 性质3: 对任何一棵二叉树, 若度数为0的结点(叶结点)的个数为n0, 度数为2的结点个数为n2, 则有n0 = n2 +1
满二叉树: 深度为k(k>=1)且有2k-1个结点的二叉树, 称为满二叉树.
完全二叉树: 如果对满二叉树按从上到下, 从左到右的顺序编号,并在最下一层删除部分结点, 如果删除的这些结点的编号是连续的, 且删除的结点含胡最大编号的结点,那么这棵二叉树就是完全二叉树.
- 性质4:含有n个结点的完全二叉树的深度为log2n +1 怎么记住:log是什么, 若有: 23 = 8, 则log28 = 3,
- 性质5: 如果将一棵n个结点的完全二叉树按层编号, 按层编号是指: 将一棵二叉树中的所有n个结点按从第一层到最大层, 每层从上到下的顺序依次标记为1, 2, , 则对任一编号为i(1<=i<=n)的结点A有:
- 若i=1, 则A是根结点, 若i>1, 则A的双亲编号Parent(A)的编号为:
i/2
- 若2*i>n, 则结点A即无左孩子, 也无右孩子, 否则A的左孩子Lchild(A)的编号为
2*i
- 若2*i +1 >n, 则结点A无右孩子; 否则A的右孩子Rchild(A)的编号为:
2*i +1
4.3 二叉树的存储结构
二叉树通常有两类存储结构: 顺序存储结构和链式存储结构.
4.3.1 二叉树的顺序存储结构
前提: 完全二叉树
顺序的存储结构适用于完全二叉树
, 若给出的是非完全二叉树, 则可以通常添加虚拟结点的方法来实现, 缺点是造成了空间的浪费
4.3.2 二叉树的链式存储结构
二叉树有不同的链式存储结构, 其中最常用的二叉链表和三叉链表
在二叉链表中, data域用于存储二叉树结点中的数据信息; lchild是指向左孩子的指针(左指针), 每个二叉链表还必须有一个指向根结点的指针, 称为根指针.
二叉链表的定义如下:
typedef struct btnode{
DataType data;
struct btnode *lchild, *rchild;
}*BTree;
三叉链表是二叉树的另一种主要的链式存储结构.
typedef struct ttnode{
DataType data;
struce ttnode *lchild, *parent, *rchild;
} *TBTree
二叉树的链式存储结构操作方便, 结点间的父子关系在二叉树链表和三叉链表中被直接表达成对应存储结点之间的指针, 因此成为二叉树最常用的存储采访神器.
4.4 二叉树的遍历
4.4.1 二叉树遍历的递归实现
- 先序遍历
若被遍历的二叉树为空, 执行空操作, 否则: **依次
**执行下列操作
(1) 访问根结点
(2) 先序遍历左子树
(3)先序遍历右子树
void preorder(BTree, bt){
if (bt !=NULL){
visit(bt);
preorder(bt->lchild);
preorder(bt->rchild);
}
}
- 中序遍历
(1) 中序遍历左子树
(2) 访问根结点
(3)中序遍历右子树
void preorder(BTree, bt){
if (bt !=NULL){
preorder(bt->lchild);
visit(bt);
preorder(bt->rchild);
}
}
- 后序遍历
(1) 后序遍历左子树
(2) 后序遍历右子树
(3) 访问根结点
void preorder(BTree, bt){
if (bt !=NULL){
preorder(bt->lchild);
preorder(bt->rchild);
visit(bt);
}
}
总结一下: 1. 永远不变的是: 先遍历左子树, 再遍历右子树,
2. 什么时间访问根结点, 导致先中后三种不同的遍历顺序
可以递归
4.4.2二叉树的层次遍历
void leveloreder(BTree, bt){
LkQue Q;
InitQueue(&Q);
if (bt != NULL){
EnQueue(&Q, bt);
while(!EmptyQueue(Q)){
p=Gethead(&Q);
OutQueue(&Q);
visit(p);
if (p->lchild != NUll) EnQueue(@Q, p->lchild);
if (p->rchild != NUll) EnQueue(@Q, p->rchild);
}
}
}
4.4.4 应用举例(p110)
由中序和后序推导出二叉树序列
4.5 树和森林