平衡二叉树学习10[1].3.2
p2->bf=0;
p=p2; }
(4)RL型调整 p A -1 -2 b
B m h+1
0
1
调整方法:先右旋转后左 旋转平衡,即先将A结点的 右孩子B结点的左子树的 根结点C结点向右上旋转 提升到B结点的位置,然 后再把该C结点向左上旋 转提升到A结点的位置。
h+1 m
C
A
B
0
-1
C
c
f
n
t
f
n
h
t p->rchild=c->lchild; /*把C的左子树挂接成A的右子树*/
0 -1
A
c
a
b h c
b
lc=p->rchild; p->rchild=lc->lchild; lc->lchild=p; p=lc;
/*lc指向B*/ /*把B结点的左子树挂接为A的右子树*/ /*A结点成为B的左孩子*/ /*p指向新的根结点*/
(3)LR型调整
p
2 1 b 0 -1 h+1 n B 0 1 c m h i A
2
属于“<“型,应该进行LR调整 先左旋转后右旋转平衡 0 -1 -2
7
LR调整后 3 0 -1 0 3 0 16 1 2 0 7 属于”/”型,应该进行LL调整 单向右旋转 9 11 0 1
0
LL调整后
-2 7
属于”\”型,应该进行RR调 整 11 单向左旋平衡 RR调整后
-1
-2
-1
3 0 9 0 -1 调整 属于”>”型,应该进行RL 11 先右旋转后左旋转平衡 0 RL调整后 7 2 0 3 0 9 16 18 1 26 0 0 26 1 0 16 -1 3 9 0 11 0
b->lchild=c->rchild; /*把C的右子树挂接成B的左子树*/ c-rchild=b; /*把B挂接成C的右子树*/
c->lchild=p;
/*把A挂接成C的左子树*/
例1 输入关键字序列{16,3,7,11,9,26,18,14,15},给 出构造一棵AVL树的步骤。
0 1 16
平衡二叉树(AVL)
结点的平衡因子(balancd factor用bf表示) :二叉树中某结点左 子树的高度与右子树的高度之差称为该结点的平衡因子. 平衡二叉树是另一种形式的二叉查找树。其特点是: 左右子树深度之差的绝对值不大于1 称有这种特性的二叉树为平衡二叉树。 在算法中,可以通过平衡因子来具体实现平衡二叉树的定义。 从平衡因子的角度可以说,若一棵二叉树中所有结点的平衡因子的绝 对值小于或等于1,则该树称为平衡二叉树。
e C
调整方法:先左旋转后右旋 转平衡,即将A结点的左孩子 (即B结点)的右子树的根 结点(即C结点)向左上旋 转提升到B结点的位置,然 后再把该C结点向右上旋转 提升到A结点的位置。 h+1
C
B
A
n
m
i
e
p->lchild=c->rchild;
/*把C的右子树挂接成A的左子树*/
b->rchild=c->lchild; /*把C的左子树挂接成B的右子树*/ c-lchild=b; c->rchild=p; /*把B挂接成C的左子树*/ /*把A挂接成C的右子树*/
7
16 1
26
18
0 属于“<“型,应该进行LR调 整 先左旋转后右旋转平衡
14
-1
15
LR调整后
11
7
18
3
9
15
26
14
16
从上述例题可以看出,每插入一个结点至多调整一次,全树都能达 到平衡.
平衡二叉树的删除
例:对下列AVL树,给出删除结点11,9,15的步骤.
11
7
18
3
9
15
26
14
16
1 38 -1 24 88
0 -1 -2
0
11
28 1
96
0
-1 0
25
0
98
1、平衡二叉树插入结点的调整方法
若向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性, 首先从根结点到该新插入结点的路径之逆向根结点方向找第一个失去平 衡的结点,然后以该失衡结点和它相邻的刚查找过的两个结点构成调整 子树(最小不平衡子树),即调整子树是指以离插入结点最近,且平衡因子 绝对值大于1的结点为根结点的子树,使之成为新的平衡子树。
/*lc指向B /*把B结点的右子树挂接为A的左子树 /*A结点成为B的右孩子 /*p指向新的根结点
(2)RR型衡:即将A的右孩子B向 左上旋转代替A成为根结点,将A结 点向左下旋转成为B的左子树的根 结点,而B的原左子树则作为A结点 的右子树。 B
p
B a h
38 24 88 -2
11
28
96
98
25
(1) LL型调整 p A 1 2
调整方法:
单向右旋平衡,即将A的左孩子 B 向右上旋转代替A成为根结点, 将A结点向右下旋转成为B的右 子树的根结点,而B的原右子树 则作为A结点的左子树。 h d e B
p
0 1
B
A
f
f
d
h
e
h
lc=p->lchild; p->lchild=lc->rchild; lc->rchild=p; p=lc;
11
7
18
3
9
15
26
待删除的结点同时有左 子树和右子树,则根据 二叉排序树的特点,可 以从其左子树中选择关 键字最大的结点或者从 其右子树中选择关键字 最小的结点放在被删去 结点的位置。 3
11 9
7
18
9
15
26
删除结点11
14 16 14 16
void Delete1(BSTNode *p,BSTNode *&r) { BSTNode *q; if(r->rchild!=NULL) else Delete1(p,r->rchild); /*找左子树最右下的结点*/ q=r; r=r->lchild; free(q);
{ p->key=r->key;
/* 找到了最右下的结点,将其关键字赋给待删除结点,并将其左子树的根结点放在 被删结点的位置上*/ } }
p
7 9 0 7 1 3 15 26 18 -2 14 15 p1 1 p2
删除结点 15 RL调整后
0 3
7
18
14
16
26
0
14
16
0
if (p2->bf=1) { p->bf=0 ; p1->bf=-1; 需作RL调整
数据结构中平衡二叉树的教学探讨与研究
数据结构中平衡二叉树的教学探讨与研究作者:朱洪浩来源:《赤峰学院学报·自然科学版》 2012年第5期朱洪浩(蚌埠学院计算机科学与技术系,安徽蚌埠 233000)摘要:平衡二叉树是对二叉排序树的一种改进,又被称为AVL树,平衡二叉树的结构较好,可以提高查找运算的速度.本文分析了权威教材和相关论文中平衡二叉树的调整方法,这些方法学生普遍反映理解和掌握较困难.据此,本文依据平衡因子和二叉排序树的特性,设计出一种基于平衡因子和二叉排序树的平衡二叉树的调整方法,该方法易于理解和掌握.关键词:二叉排序树;平衡因子;平衡二叉树中图分类号:TP311.12 文献标识码:A 文章编号:1673-260X(2012)03-0019-031 引言数据结构课程是计算机及相关专业的核心课程,是程序设计的重要理论技术基础[1].在动态查找表中,平衡二叉树被广泛的应用,平衡二叉树又称AVL树,它是由Adel,son-Vel,skii和Landis两位数学家于1962年提出并用他们的名字来命名的.平衡二叉树或者是一棵空树,或者是满足下列条件的二叉排序树:二叉排序树的所有结点的平衡因子为-1、0和1.所谓平衡因子BF(Balance Factor)可定义为某结点左子树的深度减去右子树的深度[2].若二叉树中任一个结点的平衡因子的绝对值大于1,则该二叉树就不是平衡二叉树.平衡二叉树在插入结点和删除结点时候,会使其变得不平衡.为此,需要对二叉排序树进行调整,使之重新变为平衡二叉树.相关教材和论文中关于平衡二叉树的调整方法较难理解,学生难以接受.笔者通过阅读大量的相关资料,并且总结教学经验,提出了一种易于理解和实用的二叉排序树转换成平衡二叉树方法.2 平衡二叉树调整方法的文献综述由于平衡二叉树的重要性,以及学生在学习平衡二叉树调整的过程中,普遍反映对用于平衡二叉树调整的四种方法较难理解,算法复杂.为此,许多学者对平衡二叉树的调整进行了大量的研究.严蔚敏、吴伟民[1]在《数据结构》(C语言版)一书中二叉排序树调整为平衡二叉树采用左旋转(LL)、右旋转(LR)、先左旋转后右旋转(LR)、先右旋转后左旋转(RL)四种旋转方法.李春葆[2]在《数据结构教程》(第2版)一书中也是采用了LL、LR、RR、RL四种旋转方法.朱宇、张红彬[3]在《平衡二叉树的选择调整算法》一文中,提出利用“中为根、小为左、大为右”的调整策略,但本质上仍然是利用旋转的思想.胡云[4]在《快速构建AVL树》一文中采用“将二叉排序树中的数据进行排序,将中点数据作为根,大于中点的数据构成右子树,小于中点的数据构成左子树,然后采用同样的方法分别对左子树和右子树进行调整.”但从作者举出的实例可以看出,该方法与传统方法得到的平衡二叉树并不一致.杜薇薇[5]等在《基于平衡因子的AVL树设计实现》一文中则从平衡因子出发,并用数学公式进行了验证了插入和删除操作.刘绍翰[6]等在《一种简化的AVL树的实现方法》一文提出了高度平衡树(HAVL)它是一种新的AVL树的数学描述.以上文献中虽然提出了较好的调整方法,但在平衡二叉树的调整基本上仍然是采用旋转的方法进行调整,并没有从根本上解决学生的困惑.笔者在教学中发现学生对二叉排序树的建立普遍能熟练掌握,并且平衡二叉树的前提必须是一棵二叉排序树,为此,本文提出了一种利用平衡因子和构建二叉排序树的方法来实现平衡二叉树的调整,从而解决了学生的困惑.3 平衡二叉树的调整方法根据平衡二叉树的定义可知,插入和删除结点造成平衡二叉树不平衡的原因是产生2或者-2的平衡因子,其实,调整的方法只需将以平衡因子为2或者-2为根结点的子二叉排序树重新找一个根结点建立新的子二叉排序树.从而使二叉排序树中的平衡因子都为-1、0或者1,即调整成为平衡二叉树.问题的关键是如何找根结点,即序列中的第一个结点,具体方法如下文所示规则.3.1 插入结点的调整插入结点时,可以利用规则一、规则二进行:规则一某结点的平衡因子为2时,把以该结点为根的树采用中序遍历的方法进行遍历,即得到一个递增的子序列,同时看该结点的左孩子的平衡因子.(1)若左孩子的平衡因子为-1时,则取该左孩子的右孩子结点,并将其移动到序列的最前面,得到一个新的序列,对该序列构造二叉排序树.(2)若左孩子的平衡因子为1时,则取该左孩子结点,并将其移动到序列的最前面,得到一个新序列,对该序列构造二叉排序树.规则二某结点的平衡因子为-2时,把以该结点为根的树采用中序遍历的方法进行遍历,即得到一个递增的子序列,同时看该结点的右孩子的平衡因子.(1)若右孩子的平衡因子为-1时,则取该右孩子结点,并将其移动到序列的最前面,得到一个新的序列,对该序列构造二叉排序树.(2)若右孩子的平衡因子为1时,则取该右孩子的左孩子结点,并将其移动到序列的最前面,得到一个新序列,对该序列构造二叉排序树.3.2 删除结点的调整删除结点时,要确定删除的结点是否是叶子结点,具体方法见规则三.规则三(1)若删除的是叶子结点,直接删除,并且自底向下查看树中的平衡因子,若发现存在平衡因子为2时,采用规则一进行调整,若平衡因子为-2时,则采用规则二进行调整.(2)若不是叶子结点,首先按照二叉排序树非叶子结点的删除方法即用该结点左子树的最大值(或者右子树的最小值)代替该结点,然后在从二叉排序树中删去它的最大值(或者最小值),同样,自底向下查看树中的平衡因子,若发现存在平衡因子为2时,采用规则一进行调整,若平衡因子为-2时,则采用规则二进行调整.4 算法描述4.1 插入结点的算法平衡二叉树的插入实现算法步骤:(1)插入结点L(L总是作为新的叶子结点插入的),插入的方法同一般的二叉排序树插入结点一样.(2)沿着插入结点L的路线返回,逐层回溯.必要时修改L的祖先的平衡因子,发现平衡因子为2或-2时,则利用规则一、规则二找到结点R.(3)把该二叉排序树进行中序遍历,得到一递增序列,并把结点R移动到该序列的最前面,然后对新形成的序列构造二叉排序树.同时检查树中其它结点,若发现平衡因子为2或-2的结点,进行调整.重复(2)(3)直到所有的结点都保持平衡为止.(4)回到(1),继续插入新的结点,直到所有的结点都插入完为止.实例1:输入关键字序列{16,3,7,11,9,26,18, 14,15},构造一棵平衡二叉树[2].4.2 删除结点的算法平衡二叉树的删除实现算法步骤:(1)用二叉排序树的删除算法找到并删除结点L.(2)从被删除结点到根结点逐层向上回溯,必要时修改L的祖先结点的平衡因子,发现平衡因子为2或-2时,则利用规则一、规则二找到结点R.(3)把该二叉排序树进行中序遍历,得到一递增序列,并把结点R移动到该序列的最前面,然后对新形成的序列构造二叉排序树.(4)如果调整之后,子树的总高度比调整前降低了,仍然要继续回溯,直到所有结点平衡因子都为-1、0、1时,即都保持平衡为止.实例2:对实例1生成的AVL树,给出删除结点11,9,14的步骤[2].5 结束语平衡二叉树的调整是数据结构教学中的重点和难点,学生在学习的过程中对该部分内容难以理解和接受,为了打破这种局面,作者通过阅读大量的资料,总结了此方法,该方法“只需找到新的根结点,重新构造成二叉排序树”即可,通过教学实践发现,本文采用的方法容易被学生理解和掌握,在教学中得到了良好的效果,得到学生的认可.参考文献:〔1〕严蔚敏,吴伟民.数据结构(C语言版)[M].北京:清华大学出版社,2007.〔2〕李春葆.数据结构教程(第2版).北京:清华大学出版社,2007.〔3〕朱宇,张红彬.平衡二叉树的选择调整算法[J].中国科学院研究生院学报,2006,23(4):527-533.〔4〕胡云.快速构建AVL树[J].安阳师范学院学报,2007(6):61-63.〔5〕杜薇薇,张翼燕,瞿春柳.基于平衡因子的AVL树设计实现[J].计算机技术与发展,2010,20(3):24-27.〔6〕刘绍翰,高天行,黄志球.一种简化的AVL树的实现方法[J].三峡大学学报(自然科学版),2011,33(1):85-87.。
数据结构平衡二叉树的操作演示
平衡二叉树操作的演示1.需求分析本程序是利用平衡二叉树,实现动态查找表的基本功能:创建表,查找、插入、删除。
具体功能:(1)初始,平衡二叉树为空树,操作界面给出创建、查找、插入、删除、合并、分裂六种操作供选择。
每种操作均提示输入关键字。
每次插入或删除一个结点后,更新平衡二叉树的显示。
(2)平衡二叉树的显示采用凹入表现形式。
(3)合并两棵平衡二叉树。
(4)把一棵二叉树分裂为两棵平衡二叉树,使得在一棵树中的所有关键字都小于或等于x,另一棵树中的任一关键字都大于x。
如下图:2.概要设计平衡二叉树是在构造二叉排序树的过程中,每当插入一个新结点时,首先检查是否因插入新结点而破坏了二叉排序树的平衡性,若是则找出其中的最小不平衡子树,在保持二叉排序树特性的前提下,调整最小不平衡子树中各结点之间的链接关系,进行相应的旋转,使之成为新的平衡子树。
具体步骤:(1)每当插入一个新结点,从该结点开始向上计算各结点的平衡因子,即计算该结点的祖先结点的平衡因子,若该结点的祖先结点的平衡因子的绝对值不超过1,则平衡二叉树没有失去平衡,继续插入结点;(2)若插入结点的某祖先结点的平衡因子的绝对值大于1,则找出其中最小不平衡子树的根结点;(3)判断新插入的结点与最小不平衡子树的根结点个关系,确定是那种类型的调整;(4)如果是LL型或RR型,只需应用扁担原理旋转一次,在旋转过程中,如果出现冲突,应用旋转优先原则调整冲突;如果是LR型或RL型,则需应用扁担原理旋转两次,第一次最小不平衡子树的根结点先不动,调整插入结点所在子树,第二次再调整最小不平衡子树,在旋转过程中,如果出现冲突,应用旋转优先原则调整冲突;(5)计算调整后的平衡二叉树中各结点的平衡因子,检验是否因为旋转而破坏其他结点的平衡因子,以及调整后平衡二叉树中是否存在平衡因子大于1的结点。
流程图3.详细设计二叉树类型定义:typedef int Status;typedef int ElemType;typedef struct BSTNode{ElemType data;int bf;struct BSTNode *lchild ,*rchild;} BSTNode,* BSTree;Status SearchBST(BSTree T,ElemType e)//查找void R_Rotate(BSTree &p)//右旋void L_Rotate(BSTree &p)//左旋void LeftBalance(BSTree &T)//插入平衡调整void RightBalance(BSTree &T)//插入平衡调整Status InsertAVL(BSTree &T,ElemType e,int &taller)//插入void DELeftBalance(BSTree &T)//删除平衡调整void DERightBalance(BSTree &T)//删除平衡调整Status Delete(BSTree &T,int &shorter)//删除操作Status DeleteAVL(BSTree &T,ElemType e,int &shorter)//删除操作void merge(BSTree &T1,BSTree &T2)//合并操作void splitBSTree(BSTree T,ElemType e,BSTree &T1,BSTree &T2)//分裂操作void PrintBSTree(BSTree &T,int lev)//凹入表显示附录源代码:#include<stdio.h>#include<stdlib.h>//#define TRUE 1//#define FALSE 0//#define OK 1//#define ERROR 0#define LH +1#define EH 0#define RH -1//二叉类型树的类型定义typedef int Status;typedef int ElemType;typedef struct BSTNode{ElemType data;int bf;//结点的平衡因子struct BSTNode *lchild ,*rchild;//左、右孩子指针} BSTNode,* BSTree;/*查找算法*/Status SearchBST(BSTree T,ElemType e){if(!T){return 0; //查找失败}else if(e == T->data ){return 1; //查找成功}else if (e < T->data){return SearchBST(T->lchild,e);}else{return SearchBST(T->rchild,e);}}//右旋void R_Rotate(BSTree &p){BSTree lc; //处理之前的左子树根结点lc = p->lchild; //lc指向的*p的左子树根结点p->lchild = lc->rchild; //lc的右子树挂接为*P的左子树lc->rchild = p;p = lc; //p指向新的根结点}//左旋void L_Rotate(BSTree &p){BSTree rc;rc = p->rchild; //rc指向的*p的右子树根结点p->rchild = rc->lchild; //rc的左子树挂接为*p的右子树rc->lchild = p;p = rc; //p指向新的根结点}//对以指针T所指结点为根结点的二叉树作左平衡旋转处理,//本算法结束时指针T指向新的根结点void LeftBalance(BSTree &T){BSTree lc,rd;lc=T->lchild;//lc指向*T的左子树根结点switch(lc->bf){ //检查*T的左子树的平衡度,并做相应的平衡处理case LH: //新结点插入在*T的左孩子的左子树,要做单右旋处理T->bf = lc->bf=EH;R_Rotate(T);break;case RH: //新结点插入在*T的左孩子的右子树上,做双旋处理rd=lc->rchild; //rd指向*T的左孩子的右子树根switch(rd->bf){ //修改*T及其左孩子的平衡因子case LH: T->bf=RH; lc->bf=EH;break;case EH: T->bf=lc->bf=EH;break;case RH: T->bf=EH; lc->bf=LH;break;}rd->bf=EH;L_Rotate(T->lchild); //对*T的左子树作左旋平衡处理R_Rotate(T); //对*T作右旋平衡处理}}//右平衡旋转处理void RightBalance(BSTree &T){BSTree rc,ld;rc=T->rchild;switch(rc->bf){case RH:T->bf= rc->bf=EH;L_Rotate(T);break;case LH:ld=rc->lchild;switch(ld->bf){case LH: T->bf=RH; rc->bf=EH;break;case EH: T->bf=rc->bf=EH;break;case RH: T->bf = EH; rc->bf=LH;break;}ld->bf=EH;R_Rotate(T->rchild);L_Rotate(T);}}//插入结点Status InsertAVL(BSTree &T,ElemType e,int &taller){//taller反应T长高与否if(!T){//插入新结点,树长高,置taller为trueT= (BSTree) malloc (sizeof(BSTNode));T->data = e;T->lchild = T->rchild = NULL;T->bf = EH;taller = 1;}else{if(e == T->data){taller = 0;return 0;}if(e < T->data){if(!InsertAVL(T->lchild,e,taller))//未插入return 0;if(taller)//已插入到*T的左子树中且左子树长高switch(T->bf){//检查*T的平衡度,作相应的平衡处理case LH:LeftBalance(T);taller = 0;break;case EH:T->bf = LH;taller = 1;break;case RH:T->bf = EH;taller = 0;break;}}else{if (!InsertAVL(T->rchild,e,taller)){return 0;}if(taller)//插入到*T的右子树且右子树增高switch(T->bf){//检查*T的平衡度case LH:T->bf = EH;taller = 0;break;case EH:T->bf = RH;taller = 1;break;case RH:RightBalance(T);taller = 0;break;}}}return 1;}void DELeftBalance(BSTree &T){//删除平衡调整BSTree lc,rd;lc=T->lchild;switch(lc->bf){case LH:T->bf = EH;//lc->bf= EH;R_Rotate(T);break;case EH:T->bf = EH;lc->bf= EH;R_Rotate(T);break;case RH:rd=lc->rchild;switch(rd->bf){case LH: T->bf=RH; lc->bf=EH;break;case EH: T->bf=lc->bf=EH;break;case RH: T->bf=EH; lc->bf=LH;break;}rd->bf=EH;L_Rotate(T->lchild);R_Rotate(T);}}void DERightBalance(BSTree &T) //删除平衡调整{BSTree rc,ld;rc=T->rchild;switch(rc->bf){case RH:T->bf= EH;//rc->bf= EH;L_Rotate(T);break;case EH:T->bf= EH;//rc->bf= EH;L_Rotate(T);break;case LH:ld=rc->lchild;switch(ld->bf){case LH: T->bf=RH; rc->bf=EH;break;case EH: T->bf=rc->bf=EH;break;case RH: T->bf = EH; rc->bf=LH;break;}ld->bf=EH;R_Rotate(T->rchild);L_Rotate(T);}}void SDelete(BSTree &T,BSTree &q,BSTree &s,int &shorter){if(s->rchild){SDelete(T,s,s->rchild,shorter);if(shorter)switch(s->bf){case EH:s->bf = LH;shorter = 0;break;case RH:s->bf = EH;shorter = 1;break;case LH:DELeftBalance(s);shorter = 0;break;}return;}T->data = s->data;if(q != T)q->rchild = s->lchild;elseq->lchild = s->lchild;shorter = 1;}//删除结点Status Delete(BSTree &T,int &shorter){ BSTree q;if(!T->rchild){q = T;T = T->lchild;free(q);shorter = 1;}else if(!T->lchild){q = T;T= T->rchild;free(q);shorter = 1;}else{SDelete(T,T,T->lchild,shorter);if(shorter)switch(T->bf){case EH:T->bf = RH;shorter = 0;break;case LH:T->bf = EH;shorter = 1;break;case RH:DERightBalance(T);shorter = 0;break;}}return 1;}Status DeleteAVL(BSTree &T,ElemType e,int &shorter){ int sign = 0;if (!T){return sign;}else{if(e == T->data){sign = Delete(T,shorter);return sign;}else if(e < T->data){sign = DeleteAVL(T->lchild,e,shorter);if(shorter)switch(T->bf){case EH:T->bf = RH;shorter = 0;break;case LH:T->bf = EH;shorter = 1;break;case RH:DERightBalance(T);shorter = 0;break;}return sign;}else{sign = DeleteAVL(T->rchild,e,shorter);if(shorter)switch(T->bf){case EH:T->bf = LH;shorter = 0;break;case RH:T->bf = EH;break;case LH:DELeftBalance(T);shorter = 0;break;}return sign;}}}//合并void merge(BSTree &T1,BSTree &T2){int taller = 0;if(!T2)return;merge(T1,T2->lchild);InsertAVL(T1,T2->data,taller);merge(T1,T2->rchild);}//分裂void split(BSTree T,ElemType e,BSTree &T1,BSTree &T2){ int taller = 0;if(!T)return;split(T->lchild,e,T1,T2);if(T->data > e)InsertAVL(T2,T->data,taller);elseInsertAVL(T1,T->data,taller);split(T->rchild,e,T1,T2);}//分裂void splitBSTree(BSTree T,ElemType e,BSTree &T1,BSTree &T2){ BSTree t1 = NULL,t2 = NULL;split(T,e,t1,t2);T1 = t1;T2 = t2;return;}//构建void CreatBSTree(BSTree &T){int num,i,e,taller = 0;printf("输入结点个数:");scanf("%d",&num);printf("请顺序输入结点值\n");for(i = 0 ;i < num;i++){printf("第%d个结点的值",i+1);scanf("%d",&e);InsertAVL(T,e,taller) ;}printf("构建成功,输入任意字符返回\n");getchar();getchar();}//凹入表形式显示方法void PrintBSTree(BSTree &T,int lev){int i;if(T->rchild)PrintBSTree(T->rchild,lev+1);for(i = 0;i < lev;i++)printf(" ");printf("%d\n",T->data);if(T->lchild)PrintBSTree(T->lchild,lev+1);void Start(BSTree &T1,BSTree &T2){int cho,taller,e,k;taller = 0;k = 0;while(1){system("cls");printf(" 平衡二叉树操作的演示 \n\n");printf("********************************\n");printf(" 平衡二叉树显示区 \n");printf("T1树\n");if(!T1 )printf("\n 当前为空树\n");else{PrintBSTree(T1,1);}printf("T2树\n");if(!T2 )printf("\n 当前为空树\n");elsePrintBSTree(T2,1);printf("\n********************************************************************* *********\n");printf("T1操作:1.创建 2.插入 3.查找 4.删除 10.分裂\n");printf("T2操作:5.创建 6.插入 7.查找 8.删除 11.分裂\n");printf(" 9.合并 T1,T2 0.退出\n");printf("*********************************************************************** *******\n");printf("输入你要进行的操作:");scanf("%d",&cho);switch(cho){case 1:CreatBSTree(T1);break;case 2:printf("请输入要插入关键字的值");scanf("%d",&e);InsertAVL(T1,e,taller) ;break;case 3:printf("请输入要查找关键字的值");scanf("%d",&e);if(SearchBST(T1,e))printf("查找成功!\n");elseprintf("查找失败!\n");printf("按任意键返回87"); getchar();getchar();break;case 4:printf("请输入要删除关键字的值"); scanf("%d",&e);if(DeleteAVL(T1,e,k))printf("删除成功!\n");elseprintf("删除失败!\n");printf("按任意键返回");getchar();getchar();break;case 5:CreatBSTree(T2);break;case 6:printf("请输入要插入关键字的值"); scanf("%d",&e);InsertAVL(T2,e,taller) ;break;case 7:printf("请输入要查找关键字的值"); scanf("%d",&e);if(SearchBST(T2,e))printf("查找成功!\n");elseprintf("查找失败!\n");printf("按任意键返回");getchar();getchar();break;case 8:printf("请输入要删除关键字的值"); scanf("%d",&e);if(DeleteAVL(T2,e,k))printf("删除成功!\n");elseprintf("删除失败!\n");printf("按任意键返回");getchar();getchar();break;case 9:merge(T1,T2);T2 = NULL;printf("合并成功,按任意键返回"); getchar();getchar();break;case 10:printf("请输入要中间值字的值"); scanf("%d",&e);splitBSTree(T1,e,T1,T2) ;printf("分裂成功,按任意键返回"); getchar();getchar();break;case 11:printf("请输入要中间值字的值"); scanf("%d",&e);splitBSTree(T2,e,T1,T2) ;printf("分裂成功,按任意键返回"); getchar();getchar();break;case 0:system("cls");exit(0);}}}main(){BSTree T1 = NULL;BSTree T2 = NULL;Start(T1,T2);}。
二叉树知识点总结
二叉树知识点总结1. 二叉树的性质1.1 二叉树的性质一:二叉树的深度二叉树的深度是指从根节点到叶子节点的最长路径长度。
对于一个空树而言,它的深度为0;对于只有一个根节点的树而言,它的深度为1。
根据定义可知,深度为k的二叉树中,叶子节点的深度值为k。
由此可知,二叉树的深度为所有叶子节点深度的最大值。
1.2 二叉树的性质二:二叉树的高度二叉树的高度是指从根节点到叶子节点的最短路径长度。
对于一个空树而言,它的高度为0;对于只有一个根节点的树而言,它的高度为1。
由此可知,二叉树的高度总是比深度大一。
1.3 二叉树的性质三:二叉树的节点数量对于一个深度为k的二叉树而言,它最多包含2^k - 1个节点。
而对于一个拥有n个节点的二叉树而言,它的深度最多为log2(n+1)。
1.4 二叉树的性质四:满二叉树满二叉树是一种特殊类型的二叉树,它的每个节点要么是叶子节点,要么拥有两个子节点。
满二叉树的性质是:对于深度为k的满二叉树而言,它的节点数量一定是2^k - 1。
1.5 二叉树的性质五:完全二叉树完全二叉树是一种特殊类型的二叉树,它的所有叶子节点都集中在树的最低两层,并且最后一层的叶子节点从左到右依次排列。
对于一个深度为k的完全二叉树而言,它的节点数量一定在2^(k-1)和2^k之间。
2. 二叉树的遍历二叉树的遍历是指按照一定的顺序访问二叉树的所有节点。
二叉树的遍历主要包括前序遍历、中序遍历和后序遍历三种。
2.1 前序遍历(Pre-order traversal)前序遍历的顺序是:根节点 -> 左子树 -> 右子树。
对于一个二叉树而言,前序遍历的结果就是按照“根-左-右”的顺序访问所有节点。
2.2 中序遍历(In-order traversal)中序遍历的顺序是:左子树 -> 根节点 -> 右子树。
对于一个二叉树而言,中序遍历的结果就是按照“左-根-右”的顺序访问所有节点。
2.3 后序遍历(Post-order traversal)后序遍历的顺序是:左子树 -> 右子树 -> 根节点。
平衡二叉树10.3.2
11
28
96 98
25
(1) LL型调整 型调整 p A 1 2
调整方法: 调整方法: 单向右旋平衡,即将 的左孩子 单向右旋平衡,即将A的左孩子 B 向右上旋转代替 成为根结点, 向右上旋转代替A成为根结点 成为根结点, 结点向右下旋转成为B的右 将A结点向右下旋转成为 的右 结点向右下旋转成为 子树的根结点, 子树的根结点,而B的原右子树 的原右子树 则作为A结点的左子树 结点的左子树. 则作为 结点的左子树. h d e B
1 38 -1 24 88
0 -1 -2
0
11
28 1
96
0
-1 0
25
0
98
1,平衡二叉树插入结点的调整方法
若向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性, 若向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性, 首先从根结点到该新插入结点的路径之逆向根结点方向找第一个失去平 衡的结点, 衡的结点,然后以该失衡结点和它相邻的刚查找过的两个结点构成调整 子树(最小不平衡子树 即调整子树是指以离插入结点最近,且平衡因子 最小不平衡子树), 子树 最小不平衡子树 ,即调整子树是指以离插入结点最近 且平衡因子 绝对值大于1的结点为根结点的子树 使之成为新的平衡子树. 的结点为根结点的子树,使之成为新的平衡子树 绝对值大于 的结点为根结点的子树 使之成为新的平衡子树. 38 24 88 -2
(2)RR型调整 型调整 p A -1 -2
调整方法: 调整方法: 单向左旋平衡:即将 的右孩子 的右孩子B向 单向左旋平衡:即将A的右孩子 向 左上旋转代替A成为根结点 成为根结点, 左上旋转代替 成为根结点,将A结 结 点向左下旋转成为B的左子树的根 点向左下旋转成为 的左子树的根 结点, 的原左子树则作为A结点 结点,而B的原左子树则作为 结点 的原左子树则作为 的右子树. 的右子树. B
详解平衡二叉树
一、平衡二叉树的概念平衡二叉树(Balanced binary tree)是由阿德尔森-维尔斯和兰迪斯(Adelson-Velskii and Landis)于1962年首先提出的,所以又称为AVL树。
定义:平衡二叉树或为空树,或为如下性质的二叉排序树:(1)左右子树深度之差的绝对值不超过1;(2)左右子树仍然为平衡二叉树.平衡因子BF=左子树深度-右子树深度.平衡二叉树每个结点的平衡因子只能是1,0,-1。
若其绝对值超过1,则该二叉排序树就是不平衡的。
如图所示为平衡树和非平衡树示意图:二、平衡二叉树算法思想若向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性。
首先要找出插入新结点后失去平衡的最小子树根结点的指针。
然后再调整这个子树中有关结点之间的链接关系,使之成为新的平衡子树。
当失去平衡的最小子树被调整为平衡子树后,原有其他所有不平衡子树无需调整,整个二叉排序树就又成为一棵平衡二叉树。
失去平衡的最小子树是指以离插入结点最近,且平衡因子绝对值大于1的结点作为根的子树。
假设用A表示失去平衡的最小子树的根结点,则调整该子树的操作可归纳为下列四种情况。
1)LL型平衡旋转法由于在A的左孩子B的左子树上插入结点F,使A的平衡因子由1增至2而失去平衡。
故需进行一次顺时针旋转操作。
即将A的左孩子B向右上旋转代替A作为根结点,A向右下旋转成为B的右子树的根结点。
而原来B的右子树则变成A的左子树。
(2)RR型平衡旋转法由于在A的右孩子C 的右子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。
故需进行一次逆时针旋转操作。
即将A的右孩子C向左上旋转代替A作为根结点,A向左下旋转成为C的左子树的根结点。
而原来C的左子树则变成A的右子树。
(3)LR型平衡旋转法由于在A的左孩子B的右子数上插入结点F,使A的平衡因子由1增至2而失去平衡。
故需进行两次旋转操作(先逆时针,后顺时针)。
即先将A结点的左孩子B的右子树的根结点D向左上旋转提升到B结点的位置,然后再把该D结点向右上旋转提升到A结点的位置。
平衡二叉树
2 -1 0 0 0
-1
-2 0 0 1
0 0
1
(b) 不平衡二叉树 图9.6 平衡与不平衡二叉树及结点的平衡因子
平衡二叉树是二叉排序树的另一种形式. 平衡二叉树 我们希望由任何初始序列构成的二叉排序 树都是平衡二叉树 平衡二叉树.因为平衡二叉树 平衡二叉树上任 平衡二叉树 平衡二叉树 1 何结点的左右子树的深度之差都不超过1, 则可以证明它的深度和logN是同数量级的 (其中N是结点的个数).由此,它的平 均查找长度也和logN同数量级.
typedef structBSTNode { ElemType data; int bf; //结点的平衡因子 结点的平衡因子 struct BSTNode *lchild, *rchild; //左,右孩子指针 左 } BSTNode, * BSTree;
算法9.7如下: 算法 如下: 如下 void R_Rotate (BSTree &p) { //对以 为根的二叉排序树作右旋处理,处理之后p指向新的树根结点, 对以*p为根的二叉排序树作右旋处理,处理之后 指向新的树根结点, 对以 为根的二叉排序树作右旋处理 指向新的树根结点 //即旋转处理之前的左子树的根结点 即旋转处理之前的左子树的根结点 lc = p->lchild; //lc指向的 的左子树根结点 指向的*p的左子树根结点 - 指向的 p->lchild = lc->rchild; //lc的右子树挂接为 的左子树 的右子树挂接为*p的左子树 - - 的右子树挂接为 lc->rchild = p; - p = lc; //p指向新的根结点 指向新的根结点 } // R_Rotate
平衡二叉树
编辑
红黑树
红黑树是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它是在1972年由Rudolf Bayer发明的,他称之为"对称二叉B树",它现代的名字是在 Leo J. Guibas 和 Robert Sedgewick 于1978年写的一篇论文中获得的。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n是树中元素的数目。
伸展树
伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。它由Daniel Sleator和Robert Tarjan创造。它的优势在于不需要记录用于平衡树的冗余信息。在伸展树上的一般操作都基于伸展操作。
SBT
Size Balanced Tree(简称SBT)是一自平衡二叉查找树,是在计算机科学中用到的一种数据结构。它是由中国广东中山纪念中学的陈启峰发明的。陈启峰于2006年底完成论文《Size Balanced Tree》,并在2007年的全国青少年信息学奥林匹克竞赛冬令营中发表。由于SBT的拼写很容易找到中文谐音,它常被中国的信息学竞赛选手和ACM/ICPC选手们戏称为“傻B树”、“Super BT”等。相比红黑树、AVL树等自平衡二叉查找树,SBT更易于实现。据陈启峰在论文中称,SBT是“目前为止速度最快的高级二叉搜索树”。SBT能在O(log n)的时间内完成所有二叉搜索树(BST)的相关操作,而与普通二叉搜索树相比,SBT仅仅加入了简洁的核心操作Maintain。由于SBT赖以保持平衡的是size域而不是其他“无用”的域,它可以很方便地实现动态顺序统计中的select和rank操作。
平衡二叉数
平衡二叉数平衡二叉数.txt昨天是作废的支票;明天是尚未兑现的期票;只有今天才是现金,才能随时兑现一切。
人总爱欺骗自己,因为那比欺骗别人更容易。
{treap示范代码:标准的代码缩进风格,优美的算法实现。
经典标程,完全掌握后水平会提高很多不改变bst的性质(在bst所有子树中均满足:左子树的所有节点<=根<=右子树的所有节点)通过旋转操作,使根的hr最小(即所有的hr构成堆的关系)}var//l[i],r[i],v[i]:i号结点的左儿子、右儿子,关键值//hr[i]:i号节点的优先值(treap所有子树中,根的hr必须是最小的)//s[i]:i号节点为根的子树节点总数l,r,hr,s,v:array[0..2000000]of longint; n,root,m:longint;procedure init;//初始化beginreadln(n);m:=0;//randomize; //考试要求程序每次运行结果必须一致,慎用。
确实要用:randomize 100; fillchar(s,sizeof(s),0);fillchar(l,sizeof(l),0);fillchar(r,sizeof(r),0);root:=0;end;//旋转是平衡二叉树的精髓,它不会改变bst的性质(左子树<=根<=右子树)//左旋使树的左子树深度+1,右子树深度-1//右旋使树的右子树深度+1,左子树深度-1 procedure l_rotate(var x:longint);inline;//左旋以x为根的子树(注意var参数及意义)vary:longint;beginy:=r[x]; //保存x的右儿子到y中r[x]:=l[y]; //将y的左儿子作为x的右儿子 l[y]:=x; //x作为y的左儿子s[y]:=s[x]; //维护旋转后的子树大小s[x]:=s[l[x]]+s[r[x]]+1;x:=y; //y为根end;procedure r_rotate(var x:longint);inline;//右旋以x为根的子树vary:longint;beginy:=l[x];l[x]:=r[y];r[y]:=x;s[y]:=s[x];s[x]:=s[l[x]]+s[r[x]]+1;x:=y;end;//插入(递归,if key<=root,则插入到左子树,否则到右子树,直到尽头再新建节点)procedure insert(var k,key:longint);inline;beginif k=0 then//已到尽头,新建节点并写入key 及随机值hrbegininc(m);v[m]:=key;s[m]:=1;hr[m]:=random(maxlongint);k:=m;//修改k,使父节点指向当前节点(修改前从父节点指向0)exit;end;inc(s[k]);if key<=v[k] then//若key<=根则插入到左子树,否则到右子树begininsert(l[k],key);//若l[k]=0,到下层则新建节点并修改l[k]=mif hr[l[k]]>hr[k] then //旋转r_rotate(k);exit;end;if key>v[k] thenbegininsert(r[k],key);if hr[r[k]]>hr[k] thenl_rotate(k);exit;end;end;(删除:在k号节点为根的子树中删除key基本方法:由于是静态结构,为了提高效率,并没真正删除若找到则删除,若没找到,则删除查找尽头的节点主程序中需判断返回值,若不等于key,重新插入key即可找到后的处理:若为叶节点,直接删除,否则,将要删除的节点左子树的最右节点(思考:为什么?)代替它)function delete(var k:longint;key:longint):longint;inline; begindec(s[k]);//维护节点总数//如果找到,或已到尽头if(key=v[k])or(l[k]=0)and(key<=v[k])or(r[k] =0)and(key>v[k]) thenbegindelete:=v[k];//返回要删除的节点(不一定=key)if (l[k]=0)or(r[k]=0) then //若左右子树只有一个,则让儿子代替根即可begink:=l[k]+r[k];//用儿子替换当前要删除的节点exit;end;v[k]:=delete(l[k],key+1);//k左右子树都有,则用左子树的最右节点替换kexit;end;if key<=v[k] then//若k<=v[k],则在左子树中删,否则,在右子树中删exit(delete(l[k],key));if key>v[k] thenexit(delete(r[k],key));end;function find(var k,key:longint):boolean;inline;//查找beginif k=0 then//递归边界exit(false);if key>v[k] thenfind:=find(r[k],key)elsefind:=(v[k]=key)or find(l[k],key); end;//key的排名(key排在第几,按从小到大的顺序) function rank(var t,key:longint):longint;inline;beginif t=0 thenexit(1);if key<=v[t] thenrank:=rank(l[t],key)elserank:=s[l[t]]+1+rank(r[t],key); end;function select(var t:longint;k:longint):longint;inline;//选择排在第k位的数beginif k=s[l[t]]+1 then//若找到第k位的节点,则返回节点key值exit(v[t]);if k<=s[l[t]] then//递归select:=select(l[t],k)elseselect:=select(r[t],k-1-s[l[t]]); end;function pred(var t,key:longint):longint;inline;//找key的前趋beginif t=0 thenexit(key);if key<=v[t] then//key<=根,原问题等价于在左子树中找keypred:=pred(l[t],key)else begin //key>根,原问题等价于在右子树中找key,但右儿子返回时,要判断你是不是key的前趋pred:=pred(r[t],key);//右子树的返回值if pred=key then //如果右子树的返回值=key说明在右子树中没找到key的前趋pred:=v[t]; //右子树没有key 的前趋,那你就是了。
二叉排序树与平衡二叉树的实现
课程设计报告设计题目:二叉排序树与平衡二叉树的实现专业班级学生学号指导教师起止时间年学期目录1.需求分析: (3)1.1课程设计题目、任务及要求: (3)1.2课程设计思想: (3)2.概要设计: (5)2.1二叉排序树的定义: (5)2.2二叉排序的存储结构: (5)2.3模块划分: (5)2.4主函数流程图: (6)3.详细设计和代码: (8)3.1二叉链表: (8)3.2顺序表: (14)4.心得与体会: (18)1.需求分析:1.1课程设计题目、任务及要求:用二叉链表作存储结构:(1)以回车('\n')为输入结束标志,输入数列L,生成一棵二叉排序树T;(2)对二叉排序树T作中序遍历,输出结果;(3)计算二叉排序树T查找成功的平均查找长度,输出结果;(4)输入元素x,查找二叉排序树T,若存在含x的结点,则删除该结点,并作中序遍历(执行操作2);否则输出信息“无x”;用顺序表(一维数组)作存储结构:(1)以回车('\n')为输入结束标志,输入数列L,生成一棵二叉排序树T;(2)对二叉排序树T作中序遍历,输出结果;(3)计算二叉排序树T查找成功的平均查找长度,输出结果;(4)输入元素x,查找二叉排序树T:若存在含x的结点,则删除该结点,并作中序遍历(执行操作2);否则输出信息“无x”;1.2课程设计思想:生成二叉排序树:建立二叉排序树采用的是边查找边插入的方式。
查找函数采用递归的方式进行查找。
查找是按照二叉排序树的性质进行的,通过比较要插入元素的关键字与当前结点关键字的大小来决定我们是遍历当前结点的哪个子树。
如果小于当前结点关键字,则遍历它的左子树;大于当前结点关键字,则遍历它的右子树;等于当前关键字,则此元素不插入原树。
我们按照这样的规律,一直向下遍历,知道它的子树为空,则返回当前结点的上一个结点。
然后利用插入函数将该元素插入原树。
中序遍历:对二叉排序树进行中序遍历采用递归函数的方式。
数据结构:第9章 查找2-二叉树和平衡二叉树
return(NULL); else
{if(t->data==x) return(t);
if(x<(t->data) return(search(t->lchild,x));
else return(search(t->lchild,x)); } }
——这种既查找又插入的过程称为动态查找。 二叉排序树既有类似于折半查找的特性,又采用了链表存储, 它是动态查找表的一种适宜表示。
注:若数据元素的输入顺序不同,则得到的二叉排序树形态 也不同!
讨论1:二叉排序树的插入和查找操作 例:输入待查找的关键字序列=(45,24,53,45,12,24,90)
二叉排序树的建立 对于已给定一待排序的数据序列,通常采用逐步插入结点的方 法来构造二叉排序树,即只要反复调用二叉排序树的插入算法 即可,算法描述为: BiTree *Creat (int n) //建立含有n个结点的二叉排序树 { BiTree *BST= NULL;
for ( int i=1; i<=n; i++) { scanf(“%d”,&x); //输入关键字序列
– 法2:令*s代替*p
将S的左子树成为S的双亲Q的右子树,用S取代p 。 若C无右子树,用C取代p。
例:请从下面的二叉排序树中删除结点P。
F P
法1:
F
P
C
PR
C
PR
CL Q
CL QL
Q SL
S PR
QL S
SL
法2:
F
PS
C
PR
CL Q
QL SL S SL
