树:
n(n≥0)个结点构成的有限集合
性质:
树中有一个称为“根(Root)”的特殊结点,用 r 表示
其余结点可分为m(m>0)个互不相交的有限集T1,T2,... ,Tm,其 中每个集合本身又是一棵树,称为原来树的“子树(SubTree)”
子树是不相交的
除了根结点外,每个结点有且仅有一个父结点;
一棵N个结点的树有N-1条边。
树的基本术语:
1. 结点的度(Degree):结点的子树个数
2. 树的度:树的所有结点中最大的度数
3. 叶结点(Leaf):度为0的结点
4. 父结点(Parent):有子树的结点是其子树 的根结点的父结点
5. 子结点(Child):若A结点是B结点的父结 点,则称B结点是A结点的子结点;子结点也 称孩子结点。
6. 兄弟结点(Sibling):具有同一父结点的各 结点彼此是兄弟结点。
7. 路径和路径长度:从结点n1到nk的路径为一 个结点序列n1 , n2 ,… , nk , ni是 ni+1的父结 点。路径所包含边的个数为路径的长度。
9. 祖先结点(Ancestor):沿树根到某一结点路 径上的所有结点都是这个结点的祖先结点。
10. 子孙结点(Descendant):某一结点的子树 中的所有结点是这个结点的子孙。
11. 结点的层次(Level):规定根结点在1层, 其它任一结点的层数是其父结点的层数加1。
12. 树的深度(Depth):树中所有结点中的最 大层次是这棵树的深度。
使用儿子-兄弟表示发可以使用二叉树表示度大于2的树
二叉树:
二叉树T:一个有穷的结点集合。
这个集合可以为空
若不为空,则它是由根结点和称为其左子树TL和右子树TR的 两个不相交的二叉树组成。
二叉树的子树有左右顺序之分
特殊二叉树:
斜二叉树
完美二叉树 /满二叉树
完全二叉树:有n个结点的二叉树,对树中结点按 从上至下、从左到右顺序进行编号, 编号为i(1 ≤ i ≤ n)结点与满二叉树 中编号为 i 结点在二叉树中位置相同
二叉树的几个重要性质:
一个二叉树第 i 层的最大结点数为: 2^(i-1),i>=1。
深度为k的二叉树有最大结点总数为: 2^(k-1),k>=1。
对任何非空二叉树 T,若n0表示叶结点的个数、n2是 度为2的非叶结点个数,那么两者满足关系n0 = n2 +1。
二叉树的抽象数据类型定义 :
类型名称:二叉树 数据对象集:一个有穷的结点集合。 若不为空,则由根结点和其左、右二叉子树组成。
操作集: BT BinTree, Item ElementType,重要操作有:
1、Boolean IsEmpty( BinTree BT ): 判别BT是否为空;
2、void Traversal( BinTree BT ):遍历,按某顺序访问每个结点;
3、BinTree CreatBinTree( ):创建一个二叉树。
二叉树的遍历:
先序----根、左子树、右子树;
中序---左子树、根、右子树;
后序---左子树、右子树、根
层次遍历,从上到下、从左到右
二叉树的存储结构:
1.顺序存储结构:
完全二叉树:按从上至下、从左到右顺序存
一般二叉树也可以采用这种结构,但会造成空间浪费……
2.链表存储
每个节点包含三个域:
data:保存节点的值
left:指针,指向左儿子
right:指针,指向右儿子
二叉树的遍历:
递归遍历:
先序
void PreOrderTraversal( BinTree BT ) {
if( BT ) {
printf(“%d”, BT->Data);
PreOrderTraversal( BT->Left );
PreOrderTraversal( BT->Right );
}
}
中序
void PreOrderTraversal( BinTree BT ) {
if( BT ) {
PreOrderTraversal( BT->Left );
printf(“%d”, BT->Data);
PreOrderTraversal( BT->Right );
}
}
后序
void PreOrderTraversal( BinTree BT ) {
if( BT ) {
PreOrderTraversal( BT->Left );
PreOrderTraversal( BT->Right );
printf(“%d”, BT->Data);
}
}
非递归遍历:
中序遍历非递归遍历算法 :
遇到一个结点,就把它压栈,并去遍历它的左子树;
当左子树遍历结束后,从栈顶弹出这个结点并访问它
然后按其右指针再去中序遍历该结点的右子树。
void InOrderTraversal( BinTree BT ) {
BinTree T=BT;
Stack S = CreatStack( MaxSize ); /*创建并初始化堆栈S*/
while( T || !IsEmpty(S) ){
while(T){ /*一直向左并将沿途结点压入堆栈*/
Push(S,T);
T = T->Left;
}
if(!IsEmpty(S)){
T = Pop(S); /*结点弹出堆栈*/
printf(“%5d”, T->Data); /*(访问)打印结点*/
T = T->Right; /*转向右子树*/
}
}
}
先序遍历非递归遍历算法 :
void InOrderTraversal( BinTree BT ) {
BinTree T=BT;
Stack S = CreatStack( MaxSize ); /*创建并初始化堆栈S*/
while( T || !IsEmpty(S) ){
while(T){ /*一直向左并将沿途结点压入堆栈*/
printf(“%5d”, T->Data); /*(访问)打印结点*/ //就移动了这一行
Push(S,T);
T = T->Left;
}
if(!IsEmpty(S)){
T = Pop(S); /*结点弹出堆栈*/
T = T->Right; /*转向右子树*/
}
}
}
层序遍历
队列实现:遍历从根结点开始,首先将根结点入队,然后开始执 行循环:结点出队、访问该结点、其左右儿子入队
先序和中序遍历序列或者后序和中序遍历序列来确定一棵二叉树
二叉搜索树:
也称二叉排序树或二叉查找树
二叉搜索树:一棵二叉树,可以为空;如果不为空,满足以下性质:
非空左子树的所有键值小于其根结点的键值。
非空右子树的所有键值大于其根结点的键值
左、右子树都是二叉搜索树。
二叉搜索树的查找:先和根节点比较,比根节点小则去左子树找,否则去右子树找,依次往下找知道找到指定的值
最大元素一定是在树的最右分枝的端结点上
最小元素一定是在树的最左分枝的端结点上
二叉搜索树的插入 :关键是要找到元素应该插入的位置,可以采用与Find类似的方法
二叉搜索树的删除,三种情况:
1.要删除的是叶结点:直接删除,并再修改其父结点指针---置为NULL
2.要删除的结点只有一个孩子结点:将其父结点的指针指向要删除结点的孩子结点
3.要删除的结点有左、右两棵子树:
用另一结点替代被删除结点:右子树的最小元素 或者 左子树的最大元素