数据结构第六章.docx
- 文档编号:26005539
- 上传时间:2023-06-17
- 格式:DOCX
- 页数:25
- 大小:26.26KB
数据结构第六章.docx
《数据结构第六章.docx》由会员分享,可在线阅读,更多相关《数据结构第六章.docx(25页珍藏版)》请在冰豆网上搜索。
数据结构第六章
第6章树和二叉树
6.1树
6.2二叉树
6.3以结点类为基础的二叉树设计
6.4二叉树类
6.5线索二叉树
6.6哈夫曼树
6.7树与二叉树的转换
6.8树的遍历
6.1树
6.1.1树的定义
树是由n(n≥0)个结点组成的有限集合T。
n=0的树称为空树;对n>0的树,有:
(1)仅有一个特殊的结点称为根结点,根结点没有前驱结点;
(2)当n>1时,除根结点外其余的结点分为m(m>0)个互不相交的有限集合T1,T2,…,Tm,其中每个集合Ti本身又是一棵结构和树类似的子树。
注意:
树的定义具有递归性,即“树中还有树”。
树的示例:
只有根结点的树一般的树
若干术语
根——即根结点(没有前驱)
叶子结点——即终端结点(没有后继)
森林——指m棵不相交的树的集合(例如删除A后的子树个数)
有序树——结点各子树从左至右有序,不能互换(左为第一)
无序树——结点各子树可互换位置。
双亲结点——即上层的那个结点(直接前驱)
孩子结点——即下层结点的子树的根(直接后继)
兄弟结点——同一双亲下的同层结点(孩子之间互称兄弟)
祖先结点——即从根到该结点所经分支的所有结点
子孙结点——即以该结点为根的子树中的所有结点
结点——即树的数据元素
结点的度——结点挂接的子树数
(有几个直接后继就是几度,亦称“次数”)
结点的层次——从根到该结点的层数(根结点算第0层)
终端结点——即度为0的结点,即叶子
分支结点——即度不为0的结点(也称为内部结点)
树的度——所有结点度中的最大值(Max{各结点的度})
树的深度——指所有结点中最大的层数(Max{各结点的层次})
(或高度)
问:
右上图中的结点数=13;树的度=3;树的深度=3
6.1.2树的抽象数据类型
数据集合:
树的结点集合,每个结点由数据元素和构造数据元素之间关系的指针组成。
操作集合:
(1)双亲结点parent():
把当前结点的双亲结点置为当前结点。
(2)左孩子结点leftChild():
把当前结点的左孩子结点置为当前结点。
(3)右兄弟结点rightSibling():
把当前结点的右兄弟结点置为当前结点。
(4)遍历树traverse(vs):
按某种遍历方法访问树的每个结点,且每个结点只访问一次。
6.1.3树的存储结构
1.双亲表示法
2.孩子表示法
3.双亲孩子表示法
4.孩子兄弟表示法
1双亲表示法
双亲表示法就是用指针表示出每个结点的双亲结点。
对于使用仿真指针的双亲表示法来说,每个结点应有两个域,一个是数据元素域,另一个是指示其双亲结点在数组中下标序号的仿真指针域。
树及其使用仿真指针的双亲表示法
2孩子表示法
孩子表示法就是用指针表示出每个结点的孩子结点。
常规指针的孩子表示法
3双亲孩子表示法
双亲孩子表示法就是用指针既表示出每个结点的双亲结点,也表示出每个结点的孩子结点。
4孩子兄弟表示法
孩子兄弟表示法就是用指针既表示出每个结点的孩子结点,也表示出每个结点的兄弟结点。
6.2二叉树
6.2.1二叉树的定义
6.2.2二叉树抽象数据类型
6.2.2二叉树的性质
6.2.3二叉树的存储结构
6.2.1 二叉树的定义
二叉树:
是n(n≥0)个结点的有限集合。
n=0的树称为空二叉树;n>0的二叉树由一个根结点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成。
逻辑结构:
一对二(1:
2)
基本特征:
①每个结点最多只有两棵子树(不存在度大于2的结点);
②左子树和右子树次序不能颠倒。
基本形态:
问:
具有3个结点的二叉树可能有几种不同形态?
注意:
二叉树与树和有序树的区别
二叉树与度数不超过2的树不同,与度数不超过2的有序树也不同。
在有序树中,虽然一个结点的儿子之间是有左右次序的,但若该结点只有一个儿子时,就无须区分其左右次序。
而在二叉树中,即使是一个儿子也有左右之分。
例如图中(a)和(b)是两棵不同的二叉树。
虽然它们与图c中的普通树(作为无序树或有序树)很相似,但它们却不能等同于这棵普通的树。
若将这3棵树均看作是有序树,则它们就是相同的了。
尽管二叉树与树有许多相似之处,但二叉树不是树的特殊情形。
满二叉树
在一棵二叉树中,如果所有分支结点都
存在左子树和右子树,并且所有叶子结点都
在同一层上,这样的二叉树称为满二叉树。
完全二叉树
如果一棵深度为k,有n个结点的二叉树
中各结点能够与深度为k的顺序编号的满二叉
树从1到n标号的结点相对应的二叉树称为完
全二叉树。
特点:
(1)所有的叶结点都出现在第k层或k-1层。
(2)若任一结点,如果其右子树的最大层次为i,则其左子树的
最大层次为i或i+1。
满二叉树与完全二叉树的区别
满二叉树是叶子一个也不少的树,而完全二叉树虽然前k-1层是满的,但最底层却允许在右边缺少连续若干个结点。
满二叉树是完全二叉树的一个特例。
为何要研究这两种特殊形式?
因为它们在顺序存储方式下可以复原。
6.2.2二叉树抽象数据类型
数据集合:
二叉树的结点集合,每个结点由数据元素和构造数据元素之间关系的指针组成。
操作集合:
(1)双亲结点parent():
(2)左孩子结点leftChild()
(3)右孩子结点rightChild()
(4)左插入结点insertLeftNode(x)
(5)右插入结点insertRightNode(x)
(6)删除左子树deleteLeftTree()
(7)删除右子树deleteRightTree()
(8)遍历二叉树traverse(vs)
6.2.3 二叉树的性质
讨论1:
第i层的结点数最多是多少?
(i≥0)2i个
性质1:
在一棵非空二叉树的第i层上至多有2i个结点(i≥0)。
讨论2:
深度为k(k≥-1)的二叉树,最多有多少个结点?
2k+1-1个
性质2:
深度为k的二叉树至多有2k+1-1个结点(k≥-1)。
1+2+22+23+24+…+2K
讨论3:
二叉树的叶子数和度为2的结点数之间有关系吗?
性质3:
对于任何一棵非空的二叉树,若度为2的结点数有n2个,则叶子数(n0)必定为n2+1(即n0=n2+1)
叶子数=2度结点数+1
对于两种特殊形式的二叉树(满二叉树和完全二叉树),还特别具备以下2个性质:
性质4:
具有n个结点的完全二叉树的深度k必为log2(n+1)-1
(假定k为0时表示此二叉树仅有根结点)
证明:
根据性质2,深度为k的二叉树最多只有2k+1-1个结点,且完全二叉树的定义是与同深度的满二叉树前面编号相同,即它的总结点数n位于k层和k-1层满二叉树容量之间,
即2(k-1)+1-1 对不等式求对数,有k 性质5: 对于一棵有n个结点的完全二叉树的结点按照从上至下和从左至右的顺序对所有结点从0开始顺序编号,则对于序号为i的结点,有: (1)如果i=0,则结点i无双亲,是二叉树的根;如果i>0,则其双亲是结点(i-1)/2(“/”表示整除)。 (2)如果2i+1≥n,则结点i为叶子结点,无左孩子;否则,其左孩子是结点2i+1。 (3)如果2i+2≥n,则结点i无右孩子;否则,其右孩子是结点2i+2。 6.2.4二叉树的存储结构 二叉树的存储结构主要有三种: 顺序存储结构、链式存储结构和仿真指针存储结构。 1.二叉树的顺序存储结构 完全二叉树的结点可按从上至下和从左至右的次序存储在一维数组中,其结点之间的关系可由公式计算得到,这就是二叉树的顺序存储结构。 下图在数组中的存储结构为: 问题: 对于一般的二叉树如何存储呢? 显然不能直接使用二叉树的顺序存储结构。 我们可以采取下面这种办法: 首先在非完全二叉树中增添一些并不存在的空结点使之变成完全二叉树的形态,然后再用顺序存储结构存储。 (a)一般二叉树(b)完全二叉树(c)在数组中的存储形式 一般二叉树的顺序存储结构 2.二叉树的链式存储结构 二叉树的链式存储结构是用指针建立二叉树中结点之间的关系。 二叉树最常用的的链式存储结构是二叉链。 二叉链存储结构的每个结点包含三个域,分别是数据域data、左孩子指针域leftChild和右孩子指针域rightChild。 二叉链存储结构中每个结点的图示结构为: 二叉链存储结构的二叉树也有不带头结点和带头结点两种。 带头结点的二叉链存储结构的二叉树见图7.7(b),不带头结点的二叉链存储结构的二叉树如图7.7(a)所示。 (a)不带头结点的二叉树(b)带头结点的二叉树 3.二叉树的仿真指针存储结构 二叉树的仿真指针存储结构是用数组存储二叉树中的结点,数组中每个结点除数据元素域外,再增加仿真指针域用于仿真常规指针建立二叉树中结点之间的关系。 1.根据右图的树回答问题: (1)这棵树的根结点是什么? (2)这棵树的叶子结点是什么? (3)结点C的度是多少? (4)这棵树的度是多少? (5)这棵树的深度是多少? (6)结点C的孩子结点是哪些? (7)结点C的双亲结点是谁? 2.若一棵度为4的树中度为1、2、3、4的结点个数分别为4、3、2、2,则该树叶子结点的个数是多少? 总结点个数是多少? 分析: 结点总数n=n0+n1+n2+n3+n4,除了根结点外,每个结点都对应一个分支,所以总分支数为n-1,而度为i的结点的分支数为i,所以有n-1=1×n1+2×n2+3×n3+4×n4。 综合两式得: n0+n1+n2+n3+n4-1=1×n1+2×n2+3×n3+4×n4 n0=n2+2n3+3n4+1=3+4+6+1=14 总结点个数为14+4+3+2+2=25 3.一棵高度为h的完全二叉树至少有2h结点。 4.一棵高度为h的完全二叉树至多有2h+1-1结点。 5.设高度为h的二叉树上只有度为0和度为2的结点,则此类二叉树中所包含的结点数至少为2h+1。 6.具有n个结点的二叉树采用二叉链存储结构,共有n+1个空指针域。 分析: 对于二叉链存储结构,n个结点的二叉树有2n个指针域,除根结点外,每个结点都由一个指针所指向,则共有n-1个非空指针域,空指针域个数=2n-(n-1)=n+1。 7.n个结点的二叉树中如果有m个叶子,则一定有n-2m+1个度为1的结点,m-1个度为2的结点。 分析: 由二叉树性质知度为2的结点个数为叶子结点数-1,则有m-1个度为2的结点。 又因为总结点数为n,则度为1的结点数为n-(m-1)-m=n-2m+1。 6.3以结点类为基础的二叉树设计 6.3.1二叉树结点类 classBiTreeNode{ privateBiTreeNodeleftChild; privateBiTreeNoderightChild; privateObjectdata; publicBiTreeNode() { leftChild=null; rightChild=null; } publicBiTreeNode(Objectitem,BiTreeNodeleft,BiTreeNoderight){ data=item; leftChild=left; rightChild=right; } publicBiTreeNodegetLeft(){ returnleftChild; } publicBiTreeNodegetRight(){ returnrightChild; } publicObjectgetData(){ returndata; } } 6.3.2二叉树的遍历 1、遍历定义——指按照某种顺序访问二叉树中的每个结点,使每个结点被访问一次且仅被访问一次。 (或指按某条搜索路线遍访每个结点且不重复)。 2、遍历用途——它是树结构插入、删除、修改、查找和排序运算的前提,是二叉树一切运算的基础和核心。 3、遍历规则———二叉树由根、左子树、右子树构成,定义为D、L、R vD、L、R的组合定义了六种可能的遍历方案: DLR,DRL,LDR,RDL,LRD,RLD v若限定先左后右,则有三种实现方案: vDLRLDRLRD v前序遍历中序遍历后序遍历 注: “前、中、后”的意思是指访问的结点D是先于子树出现还是后于子树出现。 以根结点为参照系 前序遍历(DLR)递归算法为: 若二叉树为空则算法结束;否则: (1)访问根结点; (2)前序遍历根结点的左子树; (3)前序遍历根结点的右子树。 中序遍历(LDR)递归算法为: 若二叉树为空则算法结束;否则: (1)中序遍历根结点的左子树; (2)访问根结点; (3)中序遍历根结点的右子树。 后序遍历(LRD)递归算法为: 若二叉树为空则算法结束;否则: (1)后序遍历根结点的左子树; (2)后序遍历根结点的右子树; (3)访问根结点。 层次遍历: 按二叉树的层次序列(即从根结点层至叶结点层),同一层中按先左子树再右子树的次序遍历二叉树。 由分析可知,二叉树层序遍历的特点是,即"先被访问的父结点的孩子结点"先于"后被访问的父结点的孩子结点"进行访问。 这样,如果把已访问的结点放在一个队列中,那么所有未被访问结点的访问次序就可以由存放在队列中的已访问结点的出队列次序决定。 因此可以借助队列实现二叉树的层序遍历。 二叉树的层序遍历算法如下: (1)初始化设置一个队列; (2)把根结点指针入队列; (3)当队列非空时,循环执行步骤(3.a)到步骤(3.c); (3.a)出队列取得当前队头结点,访问该结点; (3.b)若该结点的左孩子结点非空,则将该结点的左孩子结点指针入队列; (3.c)若该结点的右孩子结点非空,则将该结点的右孩子结点指针入队列; (4)结束。 publicstaticvoidlevelOrder(BiTreeNodet,Visitvs){ //层序遍历二叉树t,访问结点操作为vs.print(t.data) LinQueueq=newLinQueue(); if(t==null)return; BiTreeNodecurr; q.append(t); while(q.notEmpty()){ curr=(BiTreeNode)q.delete(); vs.print(curr.data); if(curr.getLeft()! =null) q.append(curr.getLeft()); if(curr.getRight()! =null) q.append(curr.getRight()); 虽然二叉树是一种非线性结构,二叉树不能像单链表那样每个结点都有一个惟一的前驱结点和惟一的后继结点,但当对一个二叉树用一种特定的遍历方法来遍历时,其遍历序列一定是线性的,且是惟一的。 例1: 前序遍历的结果是: 中序遍历的结果是: 后序遍历的结果是: 口诀: DLR—前序遍历,即先根再左再右 LDR—中序遍历,即先左再根再右 LRD—后序遍历,即先左再右再根 例2: 用二叉树表示算术表达式 前序遍历结果 +**/ABCDE —前缀表示法 中序遍历结果 A/B*C*D+E —中缀表示法 后序遍历结果 AB/C*D*E+ —后缀表示法 层次遍历结果 +*E*D/CAB 二叉树的遍历方法和二叉树的结构 由于二叉树是非线性结构,每个结点会有零个、一个或两个孩子结点,所以一个二叉树的遍历序列不能决定一棵二叉树。 但是前序(或后序)和中序遍历序列的组合可以惟一确定一棵二叉树。 而前序和后序遍历则不能。 已知一棵二叉树的中序序列和后序序列分别是BDCEAFHG和DECBHGFA,请画出这棵二叉树。 分析: ①由后序遍历特征,根结点必在后序序列尾部(即A); ②由中序遍历特征,根结点必在其中间,而且其左部必全部是左子树的子孙(即BDCE),其右部必全部是右子树的子孙(即FHG); ③继而,根据后序中的DECB子树可确定B为A的左孩子,根据HGF子串可确定F为A的右孩子;以此类推。 已知中序遍历: BDCEAFHG 已知后序遍历: DECBHGFA 前序遍历: ABDGCEF 中序遍历: DGBAECF 访问结点: 在二叉树遍历算法中,“访问该结点”表示遍历到二叉树的某个结点时,对该结点进行的具体操作。 这样的具体操作会随着问题的不同而不同。 classVisit { publicvoidprint(Objectitem) { Console.Write(item+""); } } 二叉树遍历操作的实现 classTraverse{ publicstaticvoidpreOrder(BiTreeNodet,Visitvs){ //前序遍历二叉树t,访问结点操作为vs.print(t.data) if(t! =null){ vs.print(t.data); preOrder(t.getLeft(),vs); preOrder(t.getRight(),vs); } } publicstaticvoidinOrder(BiTreeNodet,Visitvs){ //中序遍历二叉树t,访问结点操作为vs.print(t.data) if(t! =null){ inOrder(t.getLeft(),vs); vs.print(t.data); inOrder(t.getRight(),vs); } } publicstaticvoidpostOrder(BiTreeNodet,Visitvs){ //后序遍历二叉树t,访问结点操作为vs.print(t.data) if(t! =null){ postOrder(t.getLeft(),vs); postOrder(t.getRight(),vs); vs.print(t.data); } } 练习 给出下图所示二叉树的前序遍历、中序遍历、后序遍历和层次遍历的结点序列。 前序遍历序列: ABDEGCFH 中序遍历序列: DBGEAFHC 后序遍历序列: DGEBHFCA 层次遍历序列: ABCDEFGH 前序遍历序列: ABCEFDGH 中序遍历序列: ECFBGDHA 后序遍历序列: EFCGHDBA 层次遍历序列: ABCDEFGH 画出和下列已知结点序列对应的二叉树: (1)该二叉树的中序遍历结点序列为DCBGEAHFIJK; (2)该二叉树的后序遍历结点序列为DCEGBFHKJIA。 6.3.3二叉树遍历的应用 1打印二叉树 把二叉树逆时针旋转90度,按照二叉树的凹入表示法打印二叉树。 显然,可把此函数设计成递归函数。 由于把二叉树逆时针旋转90度后,在屏幕上方的首先是右子树,然后是根结点,最后是左子树,所以打印二叉树算法是一种特殊的中序遍历算法。 publicstaticvoidprintBiTree(BiTreeNoderoot,intlevel) { if(root! =null) { printBiTree(root.getRight(),level+1); if(level! =0) { for(inti=0;i<6*(level-1);i++) { Console.Write(""); } Console.Write("---"); } Console.WriteLine(root.data); printBiTree(root.getLeft(),level+1); } } printBiTree(A,0)printBiTree(C,1)printBiTree(null,2) printBiTree(C,1)printBiTree(null,2) 输出CprintBiTree(null,2) printBiTree(null,2) 输出AprintBiTree(B,1)printBiTree(null,2) printBiTree(B,1)printBiTree(null,2) 输出BprintBiTree(null,2) printBiTree(null,2) 2查找数据元素 在二叉树中查找数据元素操作的要求是: 在以t为根结点的二叉树中查找数据元素x,若查找到数据元素x时返回该结点;若查找不到数据元素x时返回空。 在二叉树t中查找数据元素x函数可设计成前序遍历函数,即首先在根结点查找,然后在左子树查找,最后在右子树查找。 publicstaticBiTreeNodesearch(BiTreeNodet,Objectx){ BiTreeNodetemp; if(t==null)returnnull; if(t.getData().Equals(x))returnt; if(t.getLeft()! =null){ temp=search(t.getLeft(),x); if(temp! =null)returntemp; } if(t.getRight()! =null){ temp=search(t.getRight(),x); if(temp! =null)returntemp; } returnnull; } 6.3.4非递归的二叉树遍历算法 非递归的前序遍历算法 非递归的二叉树前序遍历算法如下: (1)初始化设置一个堆栈; (2)把根结点指针入栈; (3)当堆栈非空时,循环执行步骤(3.a)到步骤(3.c); (3.a)出栈取得栈顶结点,访问该结点; (3.b)若该结点的右孩子结点非空,则将该结点的右孩子结点指针入栈; (3.c)若该结点的左孩子结点非空,则将该结点的左孩子结点指针入栈; (4)结束。 publicstaticvoidpreOrderNoRecur(BiTreeNoderoot) { LinStacks=newLinStack(); if(root==null)return; BiTreeNodecurr; s.push(root); while(s.notEmpty()) { curr=
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 数据结构 第六