数据结构详细教案树与二叉树资料.docx
- 文档编号:28564246
- 上传时间:2023-07-19
- 格式:DOCX
- 页数:41
- 大小:471.09KB
数据结构详细教案树与二叉树资料.docx
《数据结构详细教案树与二叉树资料.docx》由会员分享,可在线阅读,更多相关《数据结构详细教案树与二叉树资料.docx(41页珍藏版)》请在冰豆网上搜索。
数据结构详细教案树与二叉树资料
数据结构详细教案——树与二叉树资料
数据结构教案
第六章树与二叉树
第6章二叉树和树
6.1树的定义和基本术语
1、树的递归定义
1)结点数n=0时,是空树
2)结点数n>0时
有且仅有一个根结点、m个互不相交的有限结点集——m棵子树
2、基本术语
结点:
叶子(终端结点)、根、内部结点(非终端结点、分支结点);
树的规模:
结点的度、树的度、结点的层次、树的高度(深度)
结点间的关系:
双亲
(1)—孩子(m),祖先—子孙,兄弟,堂兄弟
兄弟间是否存在次序:
无序树、有序树
去掉根结点
非空树森林
引入一个根结点
3、树的抽象数据类型定义
树特有的操作:
查找:
双亲、最左的孩子、右兄弟
结点的度不定,给出这两种操作可以查找到一个结点的全部孩子
插入、删除:
孩子
遍历:
存在一对多的关系,给出一种有规律的方法遍历(有且仅访问一次)树中的结点
ADTTree{
数据对象:
D={ai|ai∈ElemSet,i=1,2,…,n,n≥0}
数据关系:
若D为空集,则称为空树;
若D仅含一个数据元素,则R为空集,否则R={H},H是如下二元关系:
(1)在D中存在唯一的称为根的数据元素root,它在关系H下无前驱;
(2)若D-{root}≠Ф,则存在D-{root}的一个划分D1,D2,…,Dm(m>0)(Di表示构成第i棵子树的结点集),对任意j≠k(1≤j,k≤m)有Dj∩Dk=Ф,且对任意的i(1≤i≤m),唯一存在数据元素xi∈Di,有
(3)对应于D-{root}的划分,H-{
基本操作:
InitTree(&T)
操作结果:
构造空树T
DestroyTree(&T)
初始条件:
树T已存在
操作结果:
销毁树T
ClearTree(&T)
初始条件:
树T已存在
操作结果:
将树T清为空树
TreeEmpty(T)
初始条件:
树T已存在
操作结果:
若T为空树,则返回TRUE,否则返回FALSE
TreeDepth(T)
初始条件:
树T已存在
操作结果:
返回树T的深度
Root(T)
初始条件:
树T已存在
操作结果:
返回T的根
Value(T,cur_e)
初始条件:
树T已存在,cur_e是T中某个结点
操作结果:
返回cur_e的值
Assign(T,&cur_e,value)
初始条件:
树T已存在,cur_e是T中某个结点
操作结果:
结点cur_e赋值为value
Parent(T,cur_e)
初始条件:
树T已存在,cur_e是T中某个结点
操作结果:
若cur_e是T的非根结点,则返回它的双亲,否则函数值为“空”
LeftChild(T,cur_e)
初始条件:
树T已存在,cur_e是T中某个结点
操作结果:
若cur_e是T的非叶子结点,则返回它的最左孩子,否则返回“空”
RightSibling(T,cur_e)
初始条件:
树T已存在,cur_e是T中某个结点
操作结果:
若cur_e有右兄弟,则返回它的右兄弟,否则返回“空”
InsertChild(&T,p,i,c)
初始条件:
树T已存在,p指向T中某个结点,1≤i≤p所指结点的度+1,非空树c与T不相交
操作结果:
插入c为T中p所指结点的第i棵子树。
DeleteChild(&T,p,i)
初始条件:
树T已存在,p指向T中某个结点,1≤i≤p所指结点的度
操作结果:
删除T中p所指结点的第i棵子树。
TraverseTree(T,visit())
初始条件:
树T已存在,visit是对结点操作的应用函数
操作结果:
按某种次序对T的每个结点调用函数visit()一次且至多一次。
一旦visit()失败,则操作失败
}ADTTree
6.2二叉树
一般树的度不定,直接考虑其操作比较困难,故首先考虑度为二的树。
这里引入二叉树。
6.2.1二叉树的定义
1、二叉树的特殊性
·0≤度≤2
·子树有左右之分(子树的个数=1或2时)
注意:
0≤度≤2的有序树≠二叉树
当某个结点只有一棵子树时,不存在序的概念
2、二叉树的抽象数据类型定义
二叉树相对树的特殊性:
最左的孩子、右兄弟左孩子、右孩子
遍历的规律性:
L(左子树)、D(根)、R(右子树)的排列上
限定为L在R前访问(有对称关系的,只须考虑其中的一种)
ADTBinaryTree{
数据对象:
D={ai|ai∈ElemSet,i=1,2,…,n,n≥0}
数据关系:
若D为空集,则称为空树;
若D仅含一个数据元素,则R为空集,否则R={H},H是如下二元关系:
(1)在D中存在唯一的称为根的数据元素root,它在关系H下无前驱;
(2)若D-{root}≠Ф,则存在D-{root}的一个划分DL,DR(左、右子树的结点集),且DL∩DR=Ф;
(3)若DL≠Ф,则DL中存在唯一元素xL,有
H;若DR≠Ф,则DR中存在唯一元素xR,有
H;
(3)(DL,{HL})是一棵符合本定义的二叉树,称为根的左子树;(DR,{HR})是一棵符合本定义的二叉树,称为根的右子树。
基本操作:
InitBiTree(&T)
操作结果:
构造空二叉树T
DestroyBiTree(&T)
初始条件:
二叉树T已存在
操作结果:
销毁二叉树T
ClearBiTree(&T)
初始条件:
二叉树T已存在
操作结果:
将二叉树T清为空树
BiTreeEmpty(T)
初始条件:
二叉树T已存在
操作结果:
若T为空二叉树,则返回TRUE,否则返回FALSE
BiTreeDepth(T)
初始条件:
二叉树T已存在
操作结果:
返回二叉树T的深度
Root(T)
初始条件:
二叉树T已存在
操作结果:
返回T的根
Value(T,cur_e)
初始条件:
二叉树T已存在,cur_e是T中某个结点
操作结果:
返回cur_e的值
Assign(T,&cur_e,value)
初始条件:
二叉树T已存在,cur_e是T中某个结点
操作结果:
结点cur_e赋值为value
Parent(T,cur_e)
初始条件:
二叉树T已存在,cur_e是T中某个结点
操作结果:
若cur_e是T的非根结点,则返回它的双亲,否则函数值为“空”
LeftChild(T,cur_e)
初始条件:
二叉树T已存在,cur_e是T中某个结点
操作结果:
若cur_e是T的非叶子结点,则返回它的左孩子,否则返回“空”
RightChild(T,cur_e)
初始条件:
二叉树T已存在,cur_e是T中某个结点
操作结果:
若cur_e有右孩子,则返回它的右孩子,否则返回“空”
LeftSibling(T,cur_e)
初始条件:
二叉树T已存在,cur_e是T中某个结点
操作结果:
返回cur_e的左兄弟,若cur_e是T的左孩子或无左兄弟,则返回“空”
RightSibling(T,cur_e)
初始条件:
二叉树T已存在,cur_e是T中某个结点
操作结果:
返回cur_e的右兄弟,若cur_e是T的右孩子或无右兄弟,则返回“空”
InsertChild(&T,p,LR,c)
初始条件:
二叉树T已存在,p指向T中某个结点,LR为0或1,非空二叉树c与T不相交
操作结果:
插入c为T中p所指结点的左或右子树。
p所指结点的原有左或右子树则成为c的右子树
DeleteChild(&T,p,LR)
初始条件:
二叉树T已存在,p指向T中某个结点,LR为0或1
操作结果:
根据LR为0或1,删除T中p所指结点的左或右子树。
PreOrderTraverse(T,visit())
初始条件:
二叉树T已存在,visit是对结点操作的应用函数
操作结果:
先序遍历T,对每个结点调用函数visit()一次且仅一次。
一旦visit()失败,则操作失败
InOrderTraverse(T,visit())
初始条件:
二叉树T已存在,visit是对结点操作的应用函数
操作结果:
中序遍历T,对每个结点调用函数visit()一次且仅一次。
一旦visit()失败,则操作失败
PostOrderTraverse(T,visit())
初始条件:
二叉树T已存在,visit是对结点操作的应用函数
操作结果:
后序遍历T,对每个结点调用函数visit()一次且仅一次。
一旦visit()失败,则操作失败
LevelOrderTraverse(T,visit())
初始条件:
二叉树T已存在,visit是对结点操作的应用函数
操作结果:
层序遍历T,对每个结点调用函数visit()一次且仅一次。
一旦visit()失败,则操作失败
}ADTBinaryTree
6.2.2二叉树的性质
1、性质1:
第i层至多有2i-1个结点(由每个结点最多只有2个孩子推出)
2、性质2:
深度为k的二叉树至多有2k-1个结点(由性质1,将各层最多的结点数累加,再结合等比数列的求和得出)
思考:
深度为k的二叉树至少有多少个结点?
(k个)深度为k的b叉树至多/至少有多少个结点?
((bk-1)/(b-1),k)
3、性质3:
n0=n2+1(ni表示二叉树中度为i的结点个数)
从两个角度考虑:
二叉树中结点的构成(根据度)n=n0+n1+n2
二叉树中充当其余结点的孩子的结点数n-1(去掉根)=n1+2×n2
满二叉树:
达到性质1,2中所述的最大值情况
完全二叉树:
去掉最下一层的结点,其余结点形成一棵满二叉树;叶子集中在最下2层(或1层),最下一层的结点总是尽可能地占满左边的位置
4、性质4:
具有n个结点的完全二叉树的深度为
5、性质5:
结点间的编号关系
考虑二叉树的顺序映像问题,寻求一种将二叉树映像为向量的方法:
对完全二叉树从上至下,从左至右,从根开始依次编号(1..n)。
孩子编号
双亲编号
求双亲
i
i/2(>0)
求孩子
左:
2*i( 2*i+1( i 右孩子编号 左孩子编号 求左兄弟 i(奇数,i>1) i-1(>0) 求右兄弟 i+1( i(偶数,i 思考: 满k叉树中结点间的编号关系? 孩子编号 双亲编号 求双亲 p (k≥2) 求第i个孩子 p·k+i-(k-1)( p 结点 结点的左兄弟 求左兄弟 p((p-1)%k≠1) p-1(>0) 求右兄弟 p+1( p((p-1)%k≠0) 6.2.3二叉树的存储结构 1、二叉树的顺序存储结构 1)方法 二叉树→补虚结点形成完全二叉树→自上而下、自左至右存储 2)类型定义 #defineMAX_TREE_SIZE100/*二叉树的最大结点数*/ typedefElemTypeSqBiTree[MAX_TREE_SIZE];/*0号单元存储根结点*/ 必须引入特殊符号表示虚结点的值 上述类型定义的缺陷: 未指明实际二叉树占用的长度,可改进为: typedefstruct{ ElemTypeelem[MAX_TREE_SIZE+1];/*1号单元存储根结点*/ intlength; }SqBiTree; 3)不足: 空间的利用率不高 如: 若深度为5且仅含有5个结点的二叉树,必须要占用24~25-1空间。 2、 二叉树的链式存储结构 1)引入辅助空间表示结点间的相对关系 双亲 (1)——孩子 (2)(如右图) lchild data rchild parent lchild data rchild 二叉链表三叉链表 若需要找指定结点的双亲,则用三叉链表可在O (1)时间内获得某结点的双亲;而用二叉链表则需从根开始,采用一定的巡查方法进行搜索。 2)二叉链表的类型定义 typedefstructBiTNode{ ElemTypedata; structBiTNode*lchild,*rchild;/*左右孩子指针*/ }BiTNode,*BiTree; 3)二叉链表的链域 若有n个结点,则共有2n个链域;其中n-1不为空,指向孩子。 4)二叉树(链式存储)的创建 输入序列与二叉树的映射关系 (1)完全二叉树的顺序映射 通过补虚结点,将一般的二叉树转变成完全二叉树,再对该完全二叉树的结点按自上而下、自左至右进行输入。 (2)二叉树的先序遍历 通过补虚结点,使二叉树中各实际结点均具有左右孩子,再对该二叉树按先序遍历进行输入。 (3)二叉树的两种遍历序列: 先序+中序,后序+中序 6.3树和森林 1、树的存储结构 1)双亲表示法 针对每一结点,附设指示其双亲位置的数据域。 采用顺序表(非顺序映像)。 #defineMAX_TREE_SIZE100/*树的最大结点数*/ typedefstructPTNode{ ElemTypedata; intparent; }PTNode; typedefstruct{ PTNodenodes[MAX_TREE_SIZE]; intn;/*结点数*/ }PTree; 2)孩子表示法 各结点的孩子数是不定的,用顺序表表示必须给出树的度的最大值,以及每一结点的实际度数,空间浪费大。 故以链表存储每一结点的所有孩子的位置信息。 typedefstructCTNode{/*孩子结点*/ intchild;/*孩子结点的位置编号*/ structCTNode*next;/*下一个孩子结点*/ }*ChildPtr; typedefstruct{ TElemTypedata; ChildPtrfirstchild;/*孩子链表的头指针*/ }CTBox; typedefstruct{ CTBoxnodes[MAX_TREE_SIZE]; intn,r;/*结点数和根的位置*/ }CTree; 3)孩子兄弟法 二叉链表表示。 针对每一结点,引入其第一个孩子和下一个右兄弟的位置域。 typedefstructCSNode{ ElemTypedata; structCSNode*firstchild,*nextsibling;/*第一个孩子、下一个兄弟指针*/ }CSNode,*CSTree; 2、森林与二叉树的转换 森林用孩子兄弟法表示,形成二叉链表,可以将它理解为一个二叉树的二叉链表; 二叉树用二叉链表表示,可以将该二叉链表理解为孩子兄弟链表,从而获得森林。 6.4二叉树的先|中|后序遍历算法 1、遍历 ·对于二叉树中的结点,有且仅访问一次 ·遍历的规律性: L(左子树)、D(根)、R(右子树)的排列上 限定L在R前访问(有对称关系的,只须考虑其中的一种) ·先(根)序遍历DLR ·中(根)序遍历LDR ·后(根)序遍历LRD 2、二叉树遍历的递归实现 二叉树的递归定义性质,决定了它的很多算法都可用递归实现,遍历就是其中之一。 对于二叉树的遍历,可以不去具体考虑各子问题(左子树、根、右子树)的遍历结果是什么,而去考虑如何由各子问题的求解结果构成原问题(二叉树)的遍历结果——递归规律的确定。 必须注意的是,当二叉树小到一定程度,即空树时,应直接给出解答——递归结束条件及处理。 三种遍历的区别(右图): 所经过的搜索路线是相同的;只是访问结点的时机不同。 每一结点在整个搜索路线中会经过3次(第一次进入到该结点、由左子树回溯到该结点、由右子树回溯到该结点),如在第一次遇到时就访问该结点,那么称之为先序;第二次经过时访问为中序;第三次经过时访问则为后序。 1)先序遍历 StatusPreOrderTraverse(BiTreeT,Status(*Visit)(ElemTypee)){ if(T! =NULL){ if(Visit(T->data)) if(PreOrderTraverse(T->lchild,Visit)) if(PreOrderTraverse(T->rchild,Visit)) returnOK; returnERROR; } elsereturnOK; } 2)后序遍历 StatusPostOrderTraverse(BiTreeT,Status(*Visit)(ElemTypee)){ if(T! =NULL){ if(PostOrderTraverse(T->lchild,Visit)) if(PostOrderTraverse(T->rchild,Visit)) if(Visit(T->data)) returnOK; returnERROR; } elsereturnOK; } 3)中序遍历 StatusInOrderTraverse(BiTreeT,Status(*Visit)(ElemTypee)){ if(T! =NULL){ if(InOrderTraverse(T->lchild,Visit)) if(Visit(T->data)) if(InOrderTraverse(T->rchild,Visit)) returnOK; returnERROR; } elsereturnOK; } 6.5先|后|中序遍历的应用扩展 利用二叉树的遍历算法,适当修改访问结点操作的内容,可以得到求解许多问题的算法。 ✧二叉树的创建(基于先序遍历) ✧二叉树的线索化 ✧查找指定结点: 在访问结点时,判断当前访问的结点是否是指定的结点,若是则返回该结点位置,否则继续遍历; ✧查找位于某种遍历次序中的第k位的结点: 遍历前,引入一计数器,用来统计已访问过的结点数,初值为0;在访问结点时,将该计数器增1,并看是否达到k,……; ✧求叶子结点的数目: 遍历前,引入叶子结点计数器,初值为0;访问结点时,将该计数器增1;遍历结束,则计数器中的值即为所求解; ✧判断二叉树中是否仅包含有度为2和0的结点: 访问结点时,判断该结点孩子的有无情况,…… ✧求指定结点的层次: 孩子结点的层次比其双亲结点层次多一; ✧求二叉树的高度: 二叉树的高度=max(左子树的高度,右子树的高度)+1 6.5.1基于先序遍历的二叉树(二叉链)的创建 【本例特征】 如何基于二叉树的先序、中序、后序遍历的递归算法进行问题求解? 【思路】 先序遍历PreOrderTraverse 创建CreateBiTree 输入 二叉链表示的二叉树的头指针T 带虚结点的先序序列ch 输入的表现方式 参数 由输入设备输入scanf(&ch) 输出 对结点的访问结果 二叉链表示的二叉树的头指针T 输出的表现方式 由输出设备输出 变参 空树(递归结束)的条件及处理 (直接求解) T==NULL ch==END_DATA(表示虚结点的值) 空 T=NULL 根结点的访问 (子问题直接求解) Visit(T->data) T=(BiTree)malloc(sizeof(BiTNode)) T->data=ch 左子树 (使用递归调用的解) PreOrderTraverse(T->lchild) CreateBiTree(T->lchild) 右子树 (使用递归调用的解) PreOrderTraverse(T->rchild) CreateBiTree(T->rchild) 【算法】见《数据结构(C语言版)》P131算法6.4。 6.5.2统计二叉树中叶子结点的数目 【本例特征】 如何通过全局变量、变参、返回值三种渠道返回处理结果? 【思路】 在遍历二叉树时,对一些特殊的结点(无左右孩子)进行计数。 可以修改遍历算法的结点访问操作为对特殊结点的判定和计数过程,需要注意的是计数器的处理方式。 可以有以下几种计数处理: ·用遍历函数的返回值传出求得的叶子结点的数目; ·为遍历函数增加一个引用参数,用来传出指定二叉树的叶子结点数目。 ·引入全局的计数器,初始为0; 此处,遍历次序的选择对本算法没有太大影响。 【算法1全局的计数器】 //n为叶子结点的计数器 intn=0; voidleaf(BiTreeT) {//利用二叉树的先序遍历 if(T! =NULL){ //访问结点->叶子的判定和计数 if(T->lchild==NULL&&T->rchild==NULL)n++; leaf(T->lchild); leaf(T->rchild); } }//调用结束,即可由n获得二叉树T的叶子结点数目,需注意下次调用前须n=0; 【算法2以函数返回值返回】 //函数值为T的叶子结点数 intleaf(BiTreeT) {//利用二叉树的中序遍历,n为局部变量 n=0; if(T! =NULL){ n=leaf(T->lchild); //访问结点->叶子结点的判定和计数 if(T->lchild==NULL&&T->rchild==NULL)n++; n+=leaf(T->rchild); } return(n); } 【算法3通过引用参数返回】 //引用参数n为T的叶子结点数 voidleaf(BiTreeT,int&n) {//利用二叉树的后序遍历 n=0; if(T! =NULL){ leaf(T->lchild,n1); leaf(T->rchild,n2); //访问结点->叶子结点的判定和计数 if(T->lchild==NULL&&T->rchild==NULL)n++; n+=n1+n2; } } 6.5.3求二叉树的高度 【思路】 ·二叉树为空时,高度为0; ·若T不为空,则其高度应为其左右子树高度的最大值再加1。 可以有以下几种处理高度值的方法: ·用遍历函数值返回指定二叉树的高度;
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构 详细 教案 二叉 资料
![提示](https://static.bdocx.com/images/bang_tan.gif)