平衡二叉树的生成ReadWord文件下载.docx
《平衡二叉树的生成ReadWord文件下载.docx》由会员分享,可在线阅读,更多相关《平衡二叉树的生成ReadWord文件下载.docx(38页珍藏版)》请在冰豆网上搜索。
●删除结点
●中序遍历
2、详细设计…………………………………………………………………………………8
F.使用的头文件
G.常量定义
H.全局变量定义
I.数据结构定义
J.部分关键函数原型说明
3、程序清单…………………………………………………………………………………10
4、程序调试与体会…………………………………………………………………………23
5、运行结果(截图)………………………………………………………………………23
四、结论………………………………………………………………………………………24
五、参考文献…………………………………………………………………………………24
摘要
树型结构是以分支关系定义的层次结构,它是一种重要的非线性结构。
树型结构在客观世界中广泛存在。
而平衡二叉树因其特性,它在查找时拥有比普通二叉树更高的效率,所以它拥有很广泛的应用。
关键词:
二叉树,平衡二叉树,查找
Abstract
Thetreestructureisdefinedwiththebranchrelationoflayerstructure,itisaimportantnonlinearstructure.Thetreestructureisanextensiveexistenceintheobjectiveworld.Andbecauseofitscharacteristic,itownshigherefficiencythangeneralbinarytreewhileinsearching,soitisusedextensively.
Keywords:
BinaryTree,BalancedBinaryTree,Search
《数据结构》课程设计
--平衡二叉树的生成设计
一、引言
平衡二叉树是数据结构中一个非常重要的概念。
它对二叉树的优化和提高查询效率有重要的作用,它是动态查找的一个非常重要方法,它在实际生产中有着广泛的应用。
二、设计目的与任务
通过本课程设计教学所要求达到的目的是:
充分理解和掌握二叉树、平衡二叉树的相关概念和知识。
掌握平衡二叉树的生成、结点删除、插入等操作过程,并编程实现从键盘上输入一系列数据(整型),建立一棵平衡二叉树,任意插入或删除一个结点后仍然要求构成平衡二叉树,并按中序遍历输出这棵平衡二叉树。
三、设计方案与实施
1、总体设计
树的概念
树(Tree)是n(n>
=0)个结点的有限集。
在任意一棵非空树中:
1)有且仅有一个特定的称为根的结点;
2)当n>
1时,其余结点可分为m(m>
0)个互不相交的有限集T1,T2......Tm,其中每一个集合又是一棵树,并且称为根的子树(SubTree)。
【例】如图1所示:
图1
图1是有8个结点的树,其中A是根,其余结点分成2个互不相交的子集:
T1={B,D},T2={C,E,F,G,H};
T1和T2都是根A的子树,且本身也是一棵树。
平衡二叉树的概念
形态匀称的二叉树称为平衡二叉树(Balancedbinarytree),其严格定义是:
若T是一棵非空二叉树,其左、右子树为TL和TR,令hl和hr分别为左、右子树的深度。
当且仅当
①TL、TR都是平衡二叉树;
②|hl-hr|≤1;
时,则T是平衡二叉树。
【例】如图2所示:
(a)平衡二叉树(b)非平衡二叉树
图2平衡二叉树与非平衡二叉树
相应地定义hl-hr为二叉平衡树的平衡因子(balancefactor)。
因此,平衡二叉树上所有结点的平衡因子可能是-1,0,1。
换言之,若一棵二叉树上任一结点的平衡因子的绝对值都不大于1,则该树是就平衡二叉树。
遍历的概念
遍历二叉树指按某条搜索路径访问树中的每个结点,使得每个结点均被访问一次,而且仅被访问一次。
由二叉树的递规定义可知,二叉树的3个基本单元组成:
根结点、左子树和右子树。
因此,若能依次遍历这3部分,便是遍历了整个二叉树。
动态平衡技术的概念
Adelson-Velskii和Landis提出了一个动态地保持二叉排序树平衡的方法,其基本思想是:
在构造二叉排序树的过程中,每当插入一个结点时,首先检查是否因插入而破坏了树的平衡性,如果是因插入结点而破坏了树的平衡性,则找出其中最小不平衡子树,在保持排序树特性的前提下,调整最小不平衡子树中各结点之间的连接关系,以达到新的平衡。
通常将这样得到的平衡二叉排序树简称为AVL树。
为了保证二叉排序树的高度为lgn,从而保证二叉排序树上实现的插入、删除和查找等基本操作的平均时间为O(lgn),在往树中插入或删除结点时,要调整树的形态来保持树的平衡。
使之既保持BST性质不变又保证树的高度在任何情况下均为O(lgn),从而确保树上的基本操作在最坏情况下的时间均为O(lgn)。
注意:
1)任一结点的左右子树的高度均相同(如满二叉树),则二叉树是完全平衡的。
通常,只要二叉树的高度为O(1gn),就可看作是平衡的。
2)平衡的二叉排序树指满足BST性质的平衡二叉树。
3)AVL树中任一结点的左、右子树的高度之差的绝对值不超过1。
在最坏情况下,n个结点的AVL树的高度约为1.44lgn。
而完全平衡的二叉树度高约为lgn,AVL树是接近最优的。
最小不平衡子树的概念
以离插入结点最近、且平衡因子绝对值大于1的结点作根结点的子树。
为了简化讨论,不妨假设二叉排序树的最小不平衡子树的根结点为A,则调整该子树的规律可归纳为下列四种情况:
图3平衡调整的4种基本类型(结点旁的数字是平衡因子)
(1)LL型:
新结点X插在A的左孩子的左子树里。
调整方法见图3(a)。
图中以B为轴心,将A结点从B的右上方转到B的右下侧,使A成为B的右孩子。
(2)RR型:
新结点X插在A的右孩子的右子树里。
调整方法见图3(b)。
图中以B为轴心,将A结点从B的左上方转到B的左下侧,使A成为B的左孩子。
(3)LR型:
新结点X插在A的左孩子的右子树里。
调整方法见图3(c)。
分为两步进行:
第一步以X为轴心,将B从X的左上方转到X的左下侧,使B成为X的左孩子,X成为A的左孩子。
第二步跟LL型一样处理(应以X为轴心)。
(4)RL型:
新结点X插在A的右孩子的左子树里。
调整方法见图3(d)。
第一步以X为轴心,将B从X的右上方转到X的右下侧,使B成为X的右孩子,X成为A的右孩子。
第二步跟RR型一样处理(应以X为轴心)。
我们希望由任何初始序列构成的二叉排序树都是AVL树。
因为AVL树上任何结点的左右子树的深度之差都不超过1,则可以证明它的深度和logN是同数量级的(其中N为结点数)。
由此,它的平均查找长度也是和logN同数量级的。
如何使构成的二叉排序树成为平衡树呢?
先看一个具体的例子(常见图4)。
假设表中关键字序列为(13,24,37,90,53)。
空树和1个结点13的树显然都是平衡的二叉树。
在插入24之后依是平衡的,只是根结点的平衡因子BF由0变为-1;
在继续插入37之后,由于结点13的BF值由-1变成-2,由此出现不平衡现象。
此时好比一根扁担出现一头重一头轻的现象,若能将扁担的支撑点由13改成至24,扁担的两头就平衡了。
由此,可以对树左一个向左逆时针“旋转”的操作,令结点24为根,而结点13为它的左子树,此时,结点13和24的平衡因子都为0,而且依保持二叉排序树的特性。
在继续插入90和53之后,由于结点37的BF值由-1变成-2,排序树中出现了新的不平衡现象,需要调整。
当此时由于结点53插在结点90结点的左子树上,因此不能和上作简单调整。
对于以结点37为根的子树来说,即要保持二叉排序树的特性,又要平衡,则必须以53作为根结点,而使37为它的左子树的根,90成为它的左子树的根。
这好比对树作了两次“旋转”操作――先向右顺时针,后向左逆时针(见图4(f)-(h)),使二叉排序树由不平衡转化为平衡。
一般情况下,假设由于在二叉排序树上插入结点而失去平衡后进行调整的规律可归纳为最小不平衡子树的概念中提到的4中操作。
图4平衡二叉树的生成过程
(a)空树;
(b)插入13;
(c)插入24;
(d)插入37;
(e)向左逆时针右旋转平衡;
(f)相继插入90和53;
(g)第一次向右顺时针旋转;
(h)第二次向左逆时针旋转平衡
4种插入中,
(1)和(3)对称,
(2)和(4)对称。
旋转操作的正确性容易由“保持二叉排序树的特性:
中序遍历所得关键字序列自小到大有序”证明之。
当平衡二叉树因插入或者删除结点而失去平衡时,仅需对最小不平衡子树进行平衡旋转处理即可。
因为经过旋转处理之后的子树深度和插入或删除之前相同,因而不影响插入或删除路径上所有祖先结点的平衡。
在平衡二叉排序树BBST上插入一个新数据元素e的递规算法可以描述如下:
(1)若BBST为空树,则插入一个数据元素为e的新结点作为BBST的根结点,树的深度增加1;
(2)若e的关键字和BBST的根结点的关键字相等,则不进行插入;
(3)若e的关键字小于BBST的根结点的关键字,而且在BBST的左子树中不存在和e有相同关键字的结点,则将e插入在BBST的左子树上,并且当插入之后的左子树深度增加1时,分别就下列情况处理之:
1.BBST的根结点的平衡因子为-1(右子树深度大于左子树深度):
则将根结点的平衡因子更改为0,BBST的深度不变;
2.BBST的根结点的平衡因子为0(左,右子树深度相等):
则将根结点的平衡因子更改为1,BBST的深度增加1;
3.BBST的根结点的平衡因子为1(左子树深度大于右子树深度):
若BBST的左子树根结点的平衡因子为1,则需要进行单向右旋平衡处理,并且在右旋处理之后将根结点和右子树根结点的平衡因子更改为0,树的深度不变;
若BBST的左子树根结点的平衡因子为-1,这需进行先向左后向右的双向旋转平衡处理,并且在旋转处理之后,修改根结点和其左、右子树根结点的平衡因子,树的深度不变;
(4)若e的关键字大于BBST的根结点的关键字,而且在BBST的右子树中不存在和e相同关键字的结点,则将e插入在BBST的右子树上,并且当插入之后的右子树深度增加1时,分别就不同情况处理。
删除结点过程与插入结点的操作类似,基本过程是:
平衡二叉树----》找到要删除的结点----》删除一个结点----》变成二叉树----》旋转----》变回平衡二叉树。
具体过程将详细设计中的代码。
右遍历的定义可知,中序遍历二叉树的递规算法可以定义为:
若二叉树为空,则空操作;
否则
1)中序遍历左子树
2)访问根结点
3)中序遍历右子树
如中序遍历图5的二叉树,结点的访问顺序为:
a→b→c→d→e→f→g
图5
2、详细设计
A.使用的头文件
#include<
stdlib.h>
iostream.h>
//cout函数要使用
iomanip.h>
//setw函数要使用
B.常量定义
#defineLH1//左高
#defineEH0//等高
#defineRH-1//右高
#defineTRUE1
#defineFALSE0
C.全局变量定义
inttaller=0;
//taller反映T长高与否
intshorter=0;
//shorter反映T变矮与否
D.数据结构定义
//根据平衡二叉树特点可以定义平衡二叉树的存储结构
//二叉排序树的类型定义
typedefstructBSTNode
{
intdata;
//结点值
intbf;
//结点的平衡因子
structBSTNode*lchild,*rchild;
//分别指向左右孩子的指针
}BSTNode,*BSTree;
//同时声明一个BSTNode和一个指针类型的*BSTree
E.部分关键函数原型说明
voidMidOrder(BSTreeT)//树的中序遍历的递归算法,一并输出它的平衡因子和左右结点值,不返回值
BSTreeR_Rotate(BSTreep)//对以p为根的二叉排序树作右旋处理,处理之p指向新的树根结点即旋转处理之前的左子树根结点
BSTreeL_Rotate(BSTreep)//对以p为根的二叉排序树作左旋处理,处理之p指向新的树根结点即旋转处理之前的右子树根结点
BSTreeLeftBalance(BSTreeT)//对以指针T所指结点为根的二叉树作左平衡旋转处理,本算法结束时指针T指向新的根结点
BSTreeRightBalance(BSTreeT)//对以指针T所指结点为根的二叉树作右平衡旋转处理,本算法结束时指针T指向新的根结点
BSTreeInsertAVL(BSTreeT,inte)//向平衡树中插入一个结点,返回插入后的新根结点
BSTreeLeftBalance1(BSTreep)//删除结点时对以指针T所指结点为根的二叉树作左平衡旋转处理,本算法结束时指针T指向新的根结点
BSTreeRightBalance1(BSTreep)//删除结点时对以指针T所指结点为根的二叉树作右平衡旋转处理,本算法结束时指针T指向新的根结点
BSTreeDelete(BSTreeq,BSTreer)//对结点进行删除处理,本算法结束时指针q指向新的根结点
BSTreeDeleteAVL(BSTreep,inte)//找到要删除的结点,并调用删除函数对其进行删除,本算法结束时指针p指向新的根结点
BSTreeCreatNode(intnodeValue)//建立关键字值为nodeValue的新结点,返回根结点
3、程序清单
//cout
//setw
//树的中序遍历的递归算法,一并输出它的平衡因子和左右结点值
voidMidOrder(BSTreeT)
//中序遍历的特点是:
当二叉树为空,则空操作;
//1.中序遍历左子树;
//2.访问根结点;
//3.中序遍历右子树。
if(T->
lchild)MidOrder(T->
lchild);
data)
{//以适当的形式格式化输出各个结点及其附加信息可以方便用户重构二叉树
cout<
<
setw(4)<
T->
data<
setw(6)<
bf;
if(T->
lchild)cout<
setw(8)<
lchild->
data;
elsecout<
"
--"
;
rchild)cout<
rchild->
endl;
}
rchild)MidOrder(T->
rchild);
}
//树的先序遍历的递归算法,一并输出它的平衡因子和左右结点值
voidRootOrder(BSTreeT)
//先序遍历的特点是:
//1.访问根结点;
//2.先序遍历左子树;
//3.先序遍历右子树。
{
}
lchild)RootOrder(T->
rchild)RootOrder(T->
//对以p为根的树作右旋处理,处理之p指向新的树根结点即旋转处理之前的左子树根结点
BSTreeR_Rotate(BSTreep)
BSTNode*lc;
//声明BSTNode*临时变量
lc=p->
lchild;
//lc指向的*p的左子树根结点
p->
lchild=lc->
rchild;
//lc的右子树挂接为*p的左子树
lc->
rchild=p;
p=lc;
//p指向新的根结点
returnp;
//返回新的根结点
//对以p为根的树作左旋处理,处理之p指向新的树根结点即旋转处理之前的右子树根结点
BSTreeL_Rotate(BSTreep)
BSTNode*rc;
rc=p->
//rc指向的*p的右子树根结点
rchild=rc->
//rc的左子树挂接为*p的右子树
rc->
lchild=p;
p=rc;
//对以指针T所指结点为根的二叉树作左平衡旋转处理,本算法结束时指针T指向新的根结点
BSTreeLeftBalance(BSTreeT)
BSTNode*lc,*rd;
lc=T->
//lc指向*T的左子树根结点
switch(lc->
bf)//检查*T的左子树平衡度,并做相应的平衡处理
caseLH:
//新结点插入在*T的左孩子的左子树上,要做单右旋处理
T->
bf=lc->
bf=EH;
T=R_Rotate(T);
break;
caseRH:
//新结点插入在*T的左孩子的右子树上,要做双旋处理
rd=lc->
//rd指向*T的左孩子的右子树根
switch(rd->
bf)//修改*T及其左孩子的平衡因子
{
caseLH:
T->
bf=RH;
lc->
break;
caseEH:
caseRH:
bf=LH;
}
rd->
lchild=L_Rotate(T->
//对*T的左孩子做左旋平衡处理
//对*T做右旋处理
returnT;
//对以指针T所指结点为根的二叉树作右平衡旋转处理,本算法结束时指针T指向新的根结点
BSTreeRightBalance(BSTreeT)
BSTreerc,ld;
rc=T->
//rc指向*T的右子树根结点
switch(rc->
bf)//检查*T的右子树平衡度,并做相应的平衡处理
//新结点插入在*T的右孩子的右子树上,要做单右旋处理
bf=rc->
T=L_Rotate(T);
//新结点插入在*T的右孩子的左子树上,要做双旋处理
ld=rc->
//ld指向*T的右孩子的左子树根
switch(ld->
bf)//修改*T及其右孩子的平衡因子
rc->
ld->
rchild=R_Rotate(T->
//对*T的右孩子做右旋平衡处理
//对*T做左旋处理
//若在平衡的二叉排序树T中不存在和e有相同关键字的结点,则插入一个数据元素为e的新结点,并返回插入后所建成的平衡二叉排序树,否则返回NULL.若因插入而使二叉数失去平衡,则作平衡旋转处理,布尔变量taller反映T长高与否
BSTr