在计算机科学中,AVL树是最先发明的自平衡二叉查找树。AVL树得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis,他们在 1962 年的论文 "An algorithm for the organization of information" 中发表了它。
一、AVL树的旋转规律
AVL树的基本操作一般涉及运做同在不平衡的二叉查找树所运做的同样的算法。但是要进行预先或随后做一次或多次所谓的"AVL旋转"。
假设由于在二叉排序树上插入结点而失去平衡的最小子树根结点的指针为a(即a是离插入点最近,且平衡因子绝对值超过1的祖先结点),则失去平衡后进行进行的规律可归纳为下列四种情况:
1. LL型
平衡二叉树某一节点的左孩子的左子树上插入一个新的节点,使得该节点不再平衡。这时只需要把树向右旋转一次即可,如图所示,原A的左孩子B变为父结点,A变为其右孩子,而原B的右子树变为A的左子树,注意旋转之后Brh是A的左子树(图上忘在A于Brh之间标实线)
2. RR型
平衡二叉树某一节点的右孩子的右子树上插入一个新的节点,使得该节点不再平衡。这时只需要把树向左旋转一次即可,如图所示,原A右孩子B变为父结点,A变为其左孩子,而原B的左子树Blh将变为A的右子树。
3. LR型
平衡二叉树某一节点的左孩子的右子树上插入一个新的节点,使得该节点不再平衡。这时需要旋转两次,仅一次的旋转是不能够使二叉树再次平衡。如图所示,在B节点按照RR型向左旋转一次之后,二叉树在A节点仍然不能保持平衡,这时还需要再向右旋转一次。
4. RL型
平衡二叉树某一节点的右孩子的左子树上插入一个新的节点,使得该节点不再平衡。同样,这时需要旋转两次,旋转方向刚好同LR型相反。
二、AVL树的基本操作
1.插入
向AVL树插入可以通过如同它是未平衡的二叉查找树一样把给定的值插入树中,接着自底向上向根节点折回,于在插入期间成为不平衡的所有节点上进行旋转来完成。因为折回到根节点的路途上最多有 1.5 乘 log n 个节点,而每次AVL 旋转都耗费恒定的时间,插入处理在整体上耗费 O(log n) 时间。
在平衡的的二叉排序树Balanced BST上插入一个新的数据元素e的递归算法可描述如下:
若BBST为空树,则插入一个数据元素为e的新结点作为BBST的根结点,树的深度增1;
若e的关键字和BBST的根结点的关键字相等,则不进行;
若e的关键字小于BBST的根结点的关键字,而且在BBST的左子树中不存在和e有相同关键字的结点,则将e插入在BBST的左子树上,并且当插入之后的左子树深度增加(+1)时,分别就下列不同情况处理之:BBST的根结点的平衡因子为-1(右子树的深度大于左子树的深度,则将根结点的平衡因子更改为 0,BBST的深度不变; BBST的根结点的平衡因子为0(左、右子树的深度相等):则将根结点的平衡因子更改为1,BBST的深度增1;BBST的根结点的平衡因子为1(左子树的深度大于右子树的深度):则若BBST的左子树根结点的平衡因子为1:则需进行单向右旋平衡处理,并且在右旋处理之后,将根结点和其右子树根结点的平衡因子更改为0,树的深度不变;若e的关键字大于BBST的根结点的关键字,而且在BBST的右子树中不存在和e有相同关键字的结点,则将e插入在BBST的右子树上,并且当插入之后的 右子树深度增加(+1)时,分别就不同情况处理之。
2.删除
从AVL树中删除可以通过把要删除的节点向下旋转成一个叶子节点,接着直接剪除这个叶子节点来完成。因为在旋转成叶子节点期间最多有 log n个节点被旋转,而每次 AVL 旋转耗费恒定的时间,删除处理在整体上耗费 O(log n) 时间。
删除操作需要考虑的情况较多,具体见代码实现吧。
3.查找
在AVL树中查找同在一般BST完全一样的进行,所以耗费 O(log n) 时间,因为AVL树总是保持平衡的。不需要特殊的准备,树的结构不会由于查询而改变。(这是与伸展树查找相对立的,它会因为查找而变更树结构。)
三、代码实现
时间仓促,对于插入、删除操作没有就各种情况配上插图,代码里面有一些注释,可以对着代码理解。日后再研究这个的时候定配上插图。
- package ly.dataStructures.tree;
- import java.util.Comparator;
- /**
- * AVL树
- * @author 无间道风云
- * 2014.0526
- * @param <AnyType>
- */
- public class AvlTree<AnyType extends Comparable<? super AnyType>> {
- private AvlNode<AnyType> root;
- private Comparator<? super AnyType> cmp;
- /********* AVL树节点数据结构定义 **********/
- private static class AvlNode<AnyType>{
- AnyType element;
- AvlNode<AnyType> left;
- AvlNode<AnyType> right;
- int height;
- AvlNode(AnyType theElement){
- this(theElement, null, null);
- }
- AvlNode(AnyType theElement, AvlNode<AnyType> lt, AvlNode<AnyType> rt){
- element = theElement;
- left = lt;
- right = rt;
- height = 0;
- }
- }
- public AvlTree(){
- root = null;
- }
- public void makeEmpty(){
- root = null;
- }
- public boolean isEmpty(){
- return root == null;
- }
- public void insert(AnyType element){
- root = insert(element, root);
- }
- public boolean contains(AnyType x){
- return contains(x, root);
- }
- public void remove(AnyType element){
- root = remove(element, root);
- }
- private int myCompare(AnyType lhs, AnyType rhs){
- if(cmp != null)
- return cmp.compare(lhs, rhs);
- else
- return ((Comparable)lhs).compareTo(rhs);
- }
- private boolean contains(AnyType x, AvlNode<AnyType> t){
- //空树处理
- if(t == null)
- return false;
- //正常情况处理
- //@方式一:对Comparable型的对象进行比较
- //int compareResult = x.compareTo(t.element);
- //@方式二:使用一个函数对象而不是要求这些项是Comparable的
- int compareResult = myCompare(x, t.element);
- if(compareResult < 0)
- return contains(x, t.left);
- else if(compareResult > 0)
- return contains(x, t.right);
- else
- return true;
- }
- private int height(AvlNode<AnyType> t){
- return t == null ? -1 : t.height;
- }
- private AvlNode<AnyType> findMin(AvlNode<AnyType> t){
- if(t == null)
- return null;
- if(t.left == null)
- return t;
- return findMin(t.left);
- }
- private AvlNode<AnyType> findMax(AvlNode<AnyType> t){
- if(t == null)
- return null;
- if(t.right == null)
- return t;
- return findMax(t.right);
- }
- private AvlNode<AnyType> insert(AnyType x, AvlNode<AnyType> t){
- if(t == null)
- return new AvlNode<AnyType>(x, null, null);
- int compareResult = myCompare(x, t.element);
- if(compareResult < 0){
- t.left = insert(x, t.left);
- if(height(t.left)-height(t.right) == 2){
- if(myCompare(x, t.left.element) < 0) //左左情况
- t = rotateWithLeftChild(t);
- else //左右情况
- t = doubleWithLeftChild(t);
- }
- }else if(compareResult > 0){
- t.right = insert(x, t.right);
- if(height(t.right)-height(t.left) == 2){
- if(myCompare(x, t.right.element) < 0) //右左情况
- t = doubleWithRightChild(t);
- else //右右情况
- t = rotateWithRightChild(t);
- }
- }
- //完了之后更新height值
- t.height = Math.max(height(t.left), height(t.right))+1;
- return t;
- }
- private AvlNode<AnyType> remove(AnyType x, AvlNode<AnyType> t){
- if(t == null)
- return null;
- int compareResult = myCompare(x, t.element);
- if(compareResult < 0){
- t.left = remove(x, t.left);
- //完了之后验证该子树是否平衡
- if(t.right != null){ //若右子树为空,则一定是平衡的,此时左子树相当对父节点深度最多为1, 所以只考虑右子树非空情况
- if(t.left == null){ //若左子树删除后为空,则需要判断右子树
- if(height(t.right)-t.height == 2){
- AvlNode<AnyType> k = t.right;
- if(k.right != null){ //右子树存在,按正常情况单旋转
- System.out.println("-----------------------------------------------------------------------------11111");
- t = rotateWithRightChild(t);
- }else{ //否则是右左情况,双旋转
- System.out.println("-----------------------------------------------------------------------------22222");
- t = doubleWithRightChild(t);
- }
- }
- }else{ //否则判断左右子树的高度差
- //左子树自身也可能不平衡,故先平衡左子树,再考虑整体
- AvlNode<AnyType> k = t.left;
- //删除操作默认用右子树上最小节点补删除的节点
- //k的左子树高度不低于k的右子树
- if(k.right != null){
- if(height(k.left)-height(k.right) == 2){
- AvlNode<AnyType> m = k.left;
- if(m.left != null){ //左子树存在,按正常情况单旋转
- System.out.println("-----------------------------------------------------------------------------33333");
- k = rotateWithLeftChild(k);
- }else{ //否则是左右情况,双旋转
- System.out.println("-----------------------------------------------------------------------------44444");
- k = doubleWithLeftChild(k);
- }
- }
- }else{
- if(height(k.left) - k.height ==2){
- AvlNode<AnyType> m = k.left;
- if(m.left != null){ //左子树存在,按正常情况单旋转
- System.out.println("-----------------------------------------------------------------------------hhhhh");
- k = rotateWithLeftChild(k);
- }else{ //否则是左右情况,双旋转
- System.out.println("-----------------------------------------------------------------------------iiiii");
- k = doubleWithLeftChild(k);
- }
- }
- }
- if(height(t.right)-height(t.left) == 2){
- //右子树自身一定是平衡的,左右失衡的话单旋转可以解决问题
- System.out.println("-----------------------------------------------------------------------------55555");
- t = rotateWithRightChild(t);
- }
- }
- }
- //完了之后更新height值
- t.height = Math.max(height(t.left), height(t.right))+1;
- }else if(compareResult > 0){
- t.right = remove(x, t.right);
- //下面验证子树是否平衡
- if(t.left != null){ //若左子树为空,则一定是平衡的,此时右子树相当对父节点深度最多为1
- if(t.right == null){ //若右子树删除后为空,则只需判断左子树
- if(height(t.left)-t.height ==2){
- AvlNode<AnyType> k = t.left;
- if(k.left != null){
- System.out.println("-----------------------------------------------------------------------------66666");
- t = rotateWithLeftChild(t);
- }else{
- System.out.println("-----------------------------------------------------------------------------77777");
- t = doubleWithLeftChild(t);
- }
- }
- }else{ //若右子树删除后非空,则判断左右子树的高度差
- //右子树自身也可能不平衡,故先平衡右子树,再考虑整体
- AvlNode<AnyType> k = t.right;
- //删除操作默认用右子树上最小节点(靠左)补删除的节点
- //k的右子树高度不低于k的左子树
- if(k.left != null){
- if(height(k.right)-height(k.left) == 2){
- AvlNode<AnyType> m = k.right;
- if(m.right != null){ //右子树存在,按正常情况单旋转
- System.out.println("-----------------------------------------------------------------------------88888");
- k = rotateWithRightChild(k);
- }else{ //否则是右左情况,双旋转
- System.out.println("-----------------------------------------------------------------------------99999");
- k = doubleWithRightChild(k);
- }
- }
- }else{
- if(height(k.right)-k.height == 2){
- AvlNode<AnyType> m = k.right;
- if(m.right != null){ //右子树存在,按正常情况单旋转
- System.out.println("-----------------------------------------------------------------------------aaaaa");
- k = rotateWithRightChild(k);
- }else{ //否则是右左情况,双旋转
- System.out.println("-----------------------------------------------------------------------------bbbbb");
- k = doubleWithRightChild(k);
- }
- }
- }
- if(height(t.left) - height(t.right) == 2){
- //左子树自身一定是平衡的,左右失衡的话单旋转可以解决问题
- System.out.println("-----------------------------------------------------------------------------ccccc");
- t = rotateWithLeftChild(t);
- }
- }
- }
- //完了之后更新height值
- t.height = Math.max(height(t.left), height(t.right))+1;
- }else if(t.left != null && t.right != null){
- //默认用其右子树的最小数据代替该节点的数据并递归的删除那个节点
- t.element = findMin(t.right).element;
- t.right = remove(t.element, t.right);
- if(t.right == null){ //若右子树删除后为空,则只需判断左子树与根的高度差
- if(height(t.left)-t.height ==2){
- AvlNode<AnyType> k = t.left;
- if(k.left != null){
- System.out.println("-----------------------------------------------------------------------------ddddd");
- t = rotateWithLeftChild(t);
- }else{
- System.out.println("-----------------------------------------------------------------------------eeeee");
- t = doubleWithLeftChild(t);
- }
- }
- }else{ //若右子树删除后非空,则判断左右子树的高度差
- //右子树自身也可能不平衡,故先平衡右子树,再考虑整体
- AvlNode<AnyType> k = t.right;
- //删除操作默认用右子树上最小节点(靠左)补删除的节点
- if(k.left != null){
- if(height(k.right)-height(k.left) == 2){
- AvlNode<AnyType> m = k.right;
- if(m.right != null){ //右子树存在,按正常情况单旋转
- System.out.println("-----------------------------------------------------------------------------fffff");
- k = rotateWithRightChild(k);
- }else{ //否则是右左情况,双旋转
- System.out.println("-----------------------------------------------------------------------------ggggg");
- k = doubleWithRightChild(k);
- }
- }
- }else{
- if(height(k.right)-k.height == 2){
- AvlNode<AnyType> m = k.right;
- if(m.right != null){ //右子树存在,按正常情况单旋转
- System.out.println("-----------------------------------------------------------------------------hhhhh");
- k = rotateWithRightChild(k);
- }else{ //否则是右左情况,双旋转
- System.out.println("-----------------------------------------------------------------------------iiiii");
- k = doubleWithRightChild(k);
- }
- }
- }
- //左子树自身一定是平衡的,左右失衡的话单旋转可以解决问题
- if(height(t.left) - height(t.right) == 2){
- System.out.println("-----------------------------------------------------------------------------jjjjj");
- t = rotateWithLeftChild(t);
- }
- }
- //完了之后更新height值
- t.height = Math.max(height(t.left), height(t.right))+1;
- }else{
- System.out.println("-----------------------------------------------------------------------------kkkkk");
- t = (t.left != null)?t.left:t.right;
- }
- return t;
- }
- //左左情况单旋转
- private AvlNode<AnyType> rotateWithLeftChild(AvlNode<AnyType> k2){
- AvlNode<AnyType> k1 = k2.left;
- k2.left = k1.right;
- k1.right = k2;
- k2.height = Math.max(height(k2.left), height(k2.right)) + 1;
- k1.height = Math.max(height(k1.left), k2.height) + 1;
- return k1; //返回新的根
- }
- //右右情况单旋转
- private AvlNode<AnyType> rotateWithRightChild(AvlNode<AnyType> k2){
- AvlNode<AnyType> k1 = k2.right;
- k2.right = k1.left;
- k1.left = k2;
- k2.height = Math.max(height(k2.left), height(k2.right)) + 1;
- k1.height = Math.max(height(k1.right), k2.height) + 1;
- return k1; //返回新的根
- }
- //左右情况
- private AvlNode<AnyType> doubleWithLeftChild(AvlNode<AnyType> k3){
- try{
- k3.left = rotateWithRightChild(k3.left);
- }catch(NullPointerException e){
- System.out.println("k.left.right为:"+k3.left.right);
- throw e;
- }
- return rotateWithLeftChild(k3);
- }
- //右左情况
- private AvlNode<AnyType> doubleWithRightChild(AvlNode<AnyType> k3){
- try{
- k3.right = rotateWithLeftChild(k3.right);
- }catch(NullPointerException e){
- System.out.println("k.right.left为:"+k3.right.left);
- throw e;
- }
- return rotateWithRightChild(k3);
- }
- }
- /*注明:由于删除操作考虑的情况甚多,代码中出现的打印信息主要为方便排错*/
测试用例如下:
- import static org.junit.Assert.*;
- import java.util.Random;
- import org.junit.Test;
- public class AvlTreeTest {
- private AvlTree<Integer> avlTree = new AvlTree<Integer>();
- @Test
- public void testInsert(){
- avlTree.insert(100);
- avlTree.insert(120);
- avlTree.insert(300);
- avlTree.insert(500);
- avlTree.insert(111);
- avlTree.insert(92);
- avlTree.insert(77);
- avlTree.insert(125);
- System.out.println(avlTree.contains(120));
- avlTree.remove(120);
- avlTree.remove(125); //需要单旋转
- System.out.println(avlTree.contains(120));
- avlTree.insert(78); //需要双旋转
- System.out.println("Insert Success !");
- }
- @Test
- public void testRotate(){
- avlTree.insert(100);
- avlTree.insert(90);
- avlTree.insert(92);
- avlTree.insert(78);
- avlTree.insert(76);
- System.out.println("Insert Success !");
- }
- /**
- * 通过较大数据进行测试,暂时还没有发现问题
- */
- @Test
- public void testAll(){
- avlTree.makeEmpty();
- Random random = new Random();
- for(int i=1;i<=1000000;i++){
- avlTree.insert(random.nextInt(1000000));
- }
- for(int i=2000000;i>=1000000;i--){
- avlTree.insert(i);
- }
- /*for(int i=700000;i>=400000;i--){
- avlTree.insert(i);
- }
- for(int i=100000;i<=200000;i++){
- avlTree.insert(i);
- }
- for(int i=400000;i<=500000;i++){
- avlTree.insert(random.nextInt(600000));
- }*/
- for(int i=200000;i<1400000;i++){
- int target = random.nextInt(1500000);
- if(avlTree.contains(target)){
- avlTree.remove(target);
- }
- }
- System.out.println("Insert Success !");
- }
- }
自平衡二叉(查找树/搜索树/排序树) binary search tree的更多相关文章
-
1.红黑树和自平衡二叉(查找)树区别 2.红黑树与B树的区别
1.红黑树和自平衡二叉(查找)树区别 1.红黑树放弃了追求完全平衡,追求大致平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来也更为简单. 2.平衡 ...
-
笔试算法题(58):二分查找树性能分析(Binary Search Tree Performance Analysis)
议题:二分查找树性能分析(Binary Search Tree Performance Analysis) 分析: 二叉搜索树(Binary Search Tree,BST)是一颗典型的二叉树,同时任 ...
-
算法:非平衡二叉搜索树(UnBalanced Binary Search Tree)
背景 很多场景下都需要将元素存储到已排序的集合中.用数组来存储,搜索效率非常高: O(log n),但是插入效率比较低:O(n).用链表来存储,插入效率和搜索效率都比较低:O(n).如何能提供插入和搜 ...
-
《数据结构与算法分析——C语言描述》ADT实现(NO.03) : 二叉搜索树/二叉查找树(Binary Search Tree)
二叉搜索树(Binary Search Tree),又名二叉查找树.二叉排序树,是一种简单的二叉树.它的特点是每一个结点的左(右)子树各结点的元素一定小于(大于)该结点的元素.将该树用于查找时,由于二 ...
-
【遍历二叉树】07恢复二叉搜索树【Recover Binary Search Tree】
开一个指针数组,中序遍历这个二叉搜索树,将节点的指针依次保存在数组里, 然后寻找两处逆序的位置, 中序便利里BST得到的是升序序列 ++++++++++++++++++++++++++++++++++ ...
-
[Swift]LeetCode669. 修剪二叉搜索树 | Trim a Binary Search Tree
Given a binary search tree and the lowest and highest boundaries as L and R, trim the tree so that a ...
-
LeetCode 669. 修剪二叉搜索树(Trim a Binary Search Tree)
669. 修剪二叉搜索树 669. Trim a Binary Search Tree 题目描述 LeetCode LeetCode669. Trim a Binary Search Tree简单 J ...
-
LeetCode 98. 验证二叉搜索树(Validate Binary Search Tree)
题目描述 给定一个二叉树,判断其是否是一个有效的二叉搜索树. 假设一个二叉搜索树具有如下特征: 节点的左子树只包含小于当前节点的数. 节点的右子树只包含大于当前节点的数. 所有左子树和右子树自身必须也 ...
-
66. 有序数组构造二叉搜索树[array to binary search tree]
[本文链接] http://www.cnblogs.com/hellogiser/p/array-to-binary-search-tree.html [题目] 编写一个程序,把一个有序整数数组放到二 ...
随机推荐
-
(数学)P、NP、NPC、NP hard问题
概念定义: P问题:能在多项式时间内解决的问题: NP问题:(Nondeterministic Polynomial time Problem)不能在多项式时间内解决或不确定能不能在多项式时间内解决, ...
-
新的三种EBS类型解析
就在前两天,创建EBS的之后页面发生了点变化,出现三种新的类型: General Purpose (SSD) Volumes Provisioned IOPS (SSD) Volumes Magnet ...
-
python项目
python实战项目: http://www.the5fire.com/category/python实战/ python基础教程中的十个项目: python项目练习一:即时标记 python项目练习 ...
-
javascript基础学习(七)
javascript之Object对象 学习要点: 创建Object对象 Object对象属性 Object对象方法 一.创建Object对象 new Object(); new Object(val ...
-
webSocket浏览器握手不成功(解决)
websocket与服务端握手会报握手不成功的错误解决方法: 首先是服务端首次收到请求要回报给客户端的报文要做处理多的不说,方法敬上: /// <summary> /// 打包请求连接数据 ...
-
jquery+jquery.pagination+php+ajax 无刷新分页
<!DOCTYPE html> <html ><head><meta http-equiv="Content-Type" content= ...
-
Git与TortoiseGit基本操作
Git与TortoiseGit基本操作 1. GitHub操作 本节先简单介绍 git 的使用与操作, 然后再介绍 TortoiseGit 的使用与操作. 先看看SVN的操作吧, 最常见的是 检出(C ...
-
Ubuntu eclipse安装
apt-get install eclipse eclipse-cdt eclipse-jdt # don't include eclipse if you have it already afte ...
-
二叉树的java实现
一.分析 一个二叉树节点有三个部分,一个是指向左子树的部分,一个是指向右子树的部分,另外一个是数据部分.可以把这个节点抽象成一个节点对象,给对象有两个节点对象属性和一个数据属性.如下图: 一个二叉树有 ...
-
mysql中什么是物理备份?
需求描述: 今天在看数据库备份,恢复的内容,就是对于一个概念,物理备份的理解,在这里记录下. 概念解释: 物理备份:就是对存储数据库内容的目录和文件的直接拷贝.简单来说,就是对物理文件的拷贝. 文档创 ...