博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
平衡二叉搜索树之AVL树
阅读量:6909 次
发布时间:2019-06-27

本文共 8606 字,大约阅读时间需要 28 分钟。

hot3.png

什么是AVL树

      介绍AVL树之前先简单了解平衡二叉搜索树。平衡二叉搜索树具有以下性质:它是一颗空树或者它的左右两个子树的高度的绝对值不超过1,并且左右两个子树都是一个平衡二叉树。AVL是最先发明的自平衡二叉树算法。在AVL中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。查找,插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或者多次旋转来重新平衡这个树。

代码实现

package test.algorithm.FastSlowPointer;import test.algorithm.FastSlowPointer.BinarySortTree.BiTNode;public class AVLTree {		class BiTNode{				static final int LH = 1;		static final int EH = 0;		static final int RH = -1;				private int data;				private BiTNode parent;				private BiTNode lChild;				private BiTNode rChild;				//平衡因子(值为-1、0、1,非这三个值必须要旋转,计算公式为左子树层次数减去右子树层次数)		private int bf;				public BiTNode(int data,int bf,BiTNode parent){			this.data = data;			this.bf = bf;			this.parent = parent;		}			}		//树的根节点	private BiTNode root;		/**      * 平衡而二叉树的插入操作      * 基本原理为:      * 1.首先如同二叉排序树一般,找到其要插入的节点的位置,并把元素插入其中;      * 2.自下向上进行回溯,回溯做两个事情:      * (1)其一是修改祖先节点的平衡因子,当插入 一个节点时只有根节点到插入节点      * 的路径中的节点的平衡因子会被改变,而且改变的原则是当插入节点在某节点(称为A)      * 的左子树 中时,A的平衡因子(称为BF)为BF+1,当插入节点在A的右子树中时A的BF-1,      * 而判断插入节点在左子树中还是右子树中只要简单的比较它与A的大小      * (2)在改变祖先节点的平衡因子的同时,找到最近一个平衡因子大于2或小于-2的节点,      * 从这个节点开始调整最小不平衡树进行旋转调整,关于如何调整见下文。      * 由于调整后,最小不平衡子树的高度与插入节点前的高度相同,故不需继续要调整祖先节点。      * 这里还有一个特殊情况,如果调整BF时,发现某个节点的BF变为0了,则停止向上继续调整,      * 因为这表明此节点中高度小的子树增加了新节点,高度不变,那么祖先节点的BF自然不变。      *       */  	public boolean insert(int key){				if(root==null){			//树为空的时候,添加到根节点			root = new BiTNode(key,0,null);			return true;		}						//当前遍历的节点		BiTNode curr = root;		//要插入节点的父节点		BiTNode parent = null;		do{			parent = curr;			if(key
curr.data){ // 往右子树找 curr = curr.rChild; }else{ // 找到,插入失败 return false; } }while(curr!=null); //插入节点 if(key
/ \ \ * ll rd ll rdl tr * / * rdl * * * 情况2(rd的BF为0) * t rd * / \ / \ * l tr 左旋后右旋 l t * / \ -------> / \ / \ * ll rd ll rdl rdr tr * / \ * rdl rdr * * * 情况3(rd的BF为-1) * t rd * / \ / \ * l tr 左旋后右旋 l t * / \ -------> / / \ * ll rd ll rdr tr * \ * rdr * * * 情况4(L等高) * t l * / 右旋处理 / \ * l ------> ll t * / \ / * ll rd rd * */ private boolean leftBalance(BiTNode t){ // 标记树的高度是否改变 boolean taller = true; BiTNode l = t.lChild; switch (l.bf) { //左高,右旋调整,旋转后树的高度减小(左左情况,见图) case BiTNode.LH : t.bf = BiTNode.EH; l.bf = BiTNode.EH; rotateRight(t); break; //右高,分情况调整 (左右情况,见图) case BiTNode.RH : BiTNode rd = l.rChild; //调整各个节点的BF switch(rd.bf){ //参见方法注释情况1 case BiTNode.LH : t.bf = BiTNode.RH; l.bf = BiTNode.EH; break; //参见方法注释情况2 case BiTNode.EH : t.bf = BiTNode.EH; l.bf = BiTNode.EH; break; //参见方法注释情况3 case BiTNode.RH : t.bf = BiTNode.EH; l.bf = BiTNode.LH; break; } //先左旋再右旋 rd.bf = BiTNode.EH; rotateLeft(t.lChild); rotateRight(t); break; //特殊情况4,这种情况在添加时不可能出现, //只在移除时可能出现,旋转之后整体树高不变 //删除root的右孩子 case BiTNode.EH : t.bf = BiTNode.LH; l.bf = BiTNode.RH; rotateRight(t); taller = false; break; } return taller; } /** * 最小旋转子树的根节点 * 向右旋转之后,p移到p的右子节点处,p的左子树l变为最小旋转子树的根节点 * l的右子节点变为p的左节点、 * 例如: p(2) l(-1) * / 右旋转 / \ * l(0) ------> / p(0) * / \ / / * lL(0) lR(0) lL(0) lR(0) */ private void rotateRight(BiTNode p){ System.out.println("绕"+p.data+"右旋"); if(p!=null){ BiTNode l = p.lChild; //p的父节点赋给l的父节点 l.parent = p.parent; //把l的右节点lR作为p的左节点 p.lChild = l.rChild; if(l.rChild!=null){ l.rChild.parent = p; } if(p.parent==null){ //p是根节点,重新设置根节点 root = l; }else if(p.parent.rChild==p){ //p是父节点右子树的根节点,重新设置左子树的根节点 p.parent.rChild = l; }else{ //p是父节点左子树的根节点,重新设置左子树的根节点 p.parent.rChild = l; } //p为l的右子树 l.rChild = p; //设置p的父节点为l p.parent = l; } } /** * 最小旋转子树的根节点 * 向左旋转之后p移到p的左子树处,p的右子树B变为此最小子树根节点, * B的左子树变为p的右子树 * 比如: p(-2) r(1) * \ 左旋转 / \ * r(0) ----> p(0) \ * / \ \ \ * rL(0) rR(0) rL(0) rR(0) * 旋转之后树的深度之差不超过1 */ private void rotateLeft(BiTNode p){ System.out.println("绕"+p.data+"左旋"); if(p!=null){ BiTNode r = p.rChild; //p的父节点赋给r的父节点 r.parent = p.parent; //把r的左节点rR作为p的右节点 p.rChild = r.lChild; if(r.lChild!=null){ r.lChild.parent = p; } if (p.parent == null) //p是根节点 ,r变为父节点,即B为父节点 root = r; else if (p.parent.lChild == p) //p是左子节点 ,p的父节点的左子树为r p.parent.lChild = r; else //如果p是右子节点 p.parent.rChild = r; //p为r的左子树 r.lChild = p; //设置p的父节点为r p.parent = r; } } /** * 做右平衡处理 * 平衡因子的调整如图: * 情况1(ld的BF为1) * t ld * / \ / \ * tl r 先右旋再左旋 t r * / \ --------> / \ \ * ld rr tl ldl rr * / * ldl * * 情况2(ld的BF为0) * t ld * / \ / \ * tl r 先右旋再左旋 t r * / \ --------> / \ / \ * ld rr tl ldl ldr rr * / \ * ldl ldr * * 情况3(ld的BF为-1) * t ld * / \ / \ * tl r 先右旋再左旋 t r * / \ --------> / / \ * ld rr tl ldr rr * \ * ldr * * 情况4(r的BF为0) * t r * \ 左旋 / \ * r -------> t rr * / \ \ * ld rr ld * */ private boolean rightBalance(BiTNode t){ //记录树的层次变化 boolean heightLower = true; BiTNode r = t.rChild; switch(r.bf){ //左高,分情况调整(右左情况) case BiTNode.LH: BiTNode ld = r.lChild; //调整各个节点的BF switch(ld.bf){ //参见方法注释情况1 case BiTNode.LH : t.bf = BiTNode.EH; r.bf = BiTNode.RH; break; //参见方法注释情况2 case BiTNode.EH : t.bf = BiTNode.EH; r.bf = BiTNode.EH; break; //参见方法注释情况3 case BiTNode.RH : t.bf = BiTNode.LH; r.bf = BiTNode.EH; break; } ld.bf = BiTNode.EH; rotateRight(t.rChild); rotateLeft(t); break; //右高,左旋调整(右右情况) case BiTNode.RH: t.bf = BiTNode.EH; r.bf = BiTNode.EH; rotateLeft(t); break; //特殊情况4 case BiTNode.EH: r.bf = BiTNode.LH; t.bf = BiTNode.RH; rotateLeft(t); heightLower = false; break; } return heightLower; } /** * 查找指定元素,如果找到返回其BiTNode对象,否则返回null */ public BiTNode search(int key){ BiTNode node = root; while(node!=null){ if(key==node.data){ return node; }else if(key

PS:下图表以四列表示四种操作,每行表示在该种情况下要进行的操作。在左左和右右的情况下,只需进行一次旋转操作;在左右和右左的情况下,需要进行两次操作。

转载于:https://my.oschina.net/u/140462/blog/284168

你可能感兴趣的文章
CRM 2013 切换显示语言
查看>>
Codeforces Round #544 (Div. 3) C. Balanced Team
查看>>
UML-对象图
查看>>
【leetcode】1037. Valid Boomerang
查看>>
一起学Android之Layout
查看>>
PHP网页计时工具——SESSION问题
查看>>
PHP 真正多线程的使用
查看>>
ip黑白名单防火墙frdev的原理与实现
查看>>
ajax全接触
查看>>
ps查看内存占用排序
查看>>
【BZOJ】4873: [Shoi2017]寿司餐厅
查看>>
leetcode-852-山脉数组的峰顶索引
查看>>
你了解HTTPS,但你可能不了解X.509
查看>>
Bootstrap 类解析
查看>>
项目总结12:bootstrap-select下拉框模糊搜索
查看>>
SCRUM 是一个用于开发和维护复杂产品的框架
查看>>
“完成”的定义
查看>>
62. ExtJS + fileuploadfield实现文件上传
查看>>
ThinkPHP/---普通传参
查看>>
计算机网络技术中的网络互连技术
查看>>