1、为什么要有AVL平衡二叉树

我们之前写过文章叫做BST二分搜索树,这种数据结构有一个缺点就是会退化为链表形式,到这导致了我们的树结构发挥不出来它应有的优势。

从上图可以发现如果按照顺序进行添加操作,那么二分搜索树就会退化为链表形式,树结构也就失去了它的意义。

AVL(Adelson-Velsky-Landis Tree)是以创造者的名字命名的。这种树结构就是一种平衡二叉树。它是最早的认为可以自平衡的一种树结构。

2、什么是AVL平衡二叉树

我们之前写过的满二叉树、完全二叉树、线段树、最大堆等等都是一种平衡树的例子(叶子节点的高度差不会大于 1 )。其实上面的平衡二叉树都是比较理想的例子。但是在AVL树中维护的平衡二叉树有所不同。对于任意一个节点,左子树和右子树的高度差不能超过 1

上面的规则相对来说更加宽松一些。比如下面这张图:

这个结构并不满足我们之前的平衡二叉树的规则,根节点的左右子树高度差不大于 1 。却满足我们上面的规则(对于任意节点)。

对于时间复杂度方面,平衡二叉树的高度和节点数量之间也是O(log (N) )。

3、AVL树的基本实现

3.1、实现的方法

首先,我们需要标注每个节点的高度信息和平衡因子,平衡因子的计算就是左子树的高度减去右子树的高度的绝对值。这样我们就可以依靠平衡因子来维护的AVL树结构。

3.2、构造函数

我们首先需要构造一个节点信息作为内部类,主要包含我们要存储的内容,左右子树的指针,还有高度信息。对于平衡因子我们可以使用左右子树的差来进行计算。 节点信息:

private class Node{ //内部类 public K key;

public V value;

public Node left, right;

public int height;

public Node(K key, V value){

this.key = key;

this.value = value;

left = null;

right = null;

height = 1; //新节点高度为 1 }

构造函数:

private Node root;

private int size;

public AVLTree(){

root = null;

size = 0;

}

3.3、基本成员函数

首先我们需要获取树结构的大小信息getSize()方法和判断是否为空isEmpty()方法。

程序实现:

public int getSize() {

return size;

}

public boolean isEmpty() {

return size == 0;

}

因为我们需要高度信息来判断树结构,所以引入getHeight()方法。

程序实现:

private int getHeight(Node node) {

if (node == null)

return 0;

return node.height;

}

为了方便我们后续的操作,我们引入getNode()方法,通过索引key值来获得节点。

程序实现:

private Node getNode(K key) {

return getNode(root, key);

}

private Node getNode(Node node, K key) {

if (node == null)

return null;

if (node.key.compareTo(key) < 0)

return getNode(node.left, key);

else if (node.key.compareTo(key) > 0)

return getNode(node.right, key);

else

return node;

}

在添加操作的时候,我们需要根据高度信息来获得平衡因子,进而判断树结构是否满足平衡树的性质。大于0:偏左,小于0:偏右

程序实现:

private int getBalanceFactor(Node node){

if (node == null)

return 0;

return getHeight(node.left) - getHeight(node.right);

}

4、左旋转和右旋转

我们知道原来平衡的树变成不平衡会是在添加元素的时候,所以在添加元素的时候我们需要维护平衡性。下面就分四种情况分别讨论:

4.1、LL 右旋转

有一种添加元素后的情况是这样的。

可以认为添加的元素在节点的左边(L)的右边(L)。 在添加元素2,后会导致树结构的平衡性破坏,对于这种情况的判断就是当前节点的平衡因子大于1(向左偏斜),并且左节点的平衡因子大于0(向左偏斜),这样就保证了这种情况的出现,向左偏斜。if (balanceFactor > 1 && getBalanceFactor(node.left) >= 0)

那么我们就需要对上面的结构进行维护。进行右旋转操作。

为了不失一般性,我们引入更复杂的情况,如下图:

按照平衡二叉树的排序规则,对元素进行右旋转(类似于 y 绕 x 右旋转),相当于降低树的高度。旋转后仍然满足二叉树的排序规则。我们可以发现相对位置发生改变的就是节点 y 和 x 的右子树 T3 。最后更新高度信息,高度发生变化的只有节点 x , y 。 程序实现:

private Node rightRotate(Node y) {

Node x = y.left; //获得旋转中心 Node T3 = x.right;

x.right = y; //进行旋转操作 y.left = T3;

y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1; //重新更新高度信息 x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;

return x;

}

4.2、RR 左旋转

我们有了上面的右旋转的概念,那么左旋转就变得简单多了,左旋转的发生的前提就是节点的平衡因子小于-1(偏右),右子树的平衡因子小于0(偏右),也就是类似于下面的结构。 可以认为添加的元素在节点的右边(R)的右边(R)。if (balanceFactor < -1 && getBalanceFactor(node.right) <= 0)

下面就是对结构进行左旋转操作,维护平衡性。

以x为中心,进行左旋转操作(类似于 y 绕 x 左旋转),需要转移的分别是 x 的左子树 T2 和节点 y 。 程序实现:

private Node leftRotate(Node y) {

Node x = y.right;

Node T2 = x.left;

x.left = y; //需要转移的元素 y.right = T2;

y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1; // 更新高度信息 x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;

return x;

}

4.3、LR 左右旋转

这种情况就是添加元素在节点的左边的右边,左右旋转指的是先进行左旋转,后进行右旋转。类似于下面这种情况。

具体细节的旋转如下:

先进行左旋转操作,结构就会变成我们之前LL的形式,然后对其进行右旋转操作即可。

4.4、RL 右左旋转

这种情况就是添加元素在节点的右边的左边,右左旋转指的是先进行右旋转,后进行左旋转。类似于下面这种情况。

具体细节的旋转如下: 先进行左旋转操作,结构就会变成我们之前LL的形式,然后对其进行右旋转操作即可。

4.5、四种情况总结

上面的四种情况完全包含了添加元素所需要的情况。

// LLif (balanceFactor > 1 && getBalanceFactor(node.left) >= 0)

return rightRotate(node);

// RRif (balanceFactor < -1 && getBalanceFactor(node.right) <= 0)

return leftRotate(node);

// LRif (balanceFactor > 1 && getBalanceFactor(node.left) < 0){

node.left = leftRotate(node.left);

return rightRotate(node);

}

// RLif (balanceFactor < -1 && getBalanceFactor(node.right) > 0){

node.right = rightRotate(node.right);

return leftRotate(node);

}

5、增删改查操作的实现

5.1、添加操纵

步骤:递归到底的时候添加元素,否则就更新元素

更新每个节点的高度信息 + 对结构进行平衡处理

private Node add(Node node, K key, V value) {

/** BST 的源代码片段 ····*/

node.height = 1 + Math.max(getHeight(node.left), getHeight(node.right));

int balanceFactor = getBalanceFactor(node);

// LL if (balanceFactor > 1 && getBalanceFactor(node.left) >= 0)

return rightRotate(node);

// RR if (balanceFactor < -1 && getBalanceFactor(node.right) <= 0)

return leftRotate(node);

// LR if (balanceFactor > 1 && getBalanceFactor(node.left) < 0){

node.left = leftRotate(node.left);

return rightRotate(node);

}

// RL if (balanceFactor < -1 && getBalanceFactor(node.right) > 0){

node.right = rightRotate(node.right);

return leftRotate(node);

}

return node;

}

5.2、删除操作

这里的删除操作,在原本BST二分搜索树的基础上进行改变,增加删除元素后对节点进行平衡后的处理,同添加操作基本相同,这里只对增加的程序片段进行展示。

private Node remove(Node node, K key) {

/** BST 的源代码片段 ····*/

if (retNode == null)

return null;

retNode.height = 1 + Math.max(getHeight(retNode.left), getHeight(retNode.right)); //更新高度值 int balanceFactor = getBalanceFactor(retNode);

// LL if (balanceFactor > 1 && getBalanceFactor(retNode.left) >= 0)

return rightRotate(retNode);

// RR if (balanceFactor < -1 && getBalanceFactor(retNode.right) <= 0)

return leftRotate(retNode);

// LR if (balanceFactor > 1 && getBalanceFactor(retNode.left) < 0){

retNode.left = leftRotate(retNode.left);

return rightRotate(retNode);

}

// RL if (balanceFactor < -1 && getBalanceFactor(retNode.right) > 0){

retNode.right = rightRotate(retNode.right);

return leftRotate(retNode);

}

return retNode;

}

5.3、查询操作

查询操作主要包含查看元素是否在树结构中,另一个就是通过key值查找对应的 value 值。两者均是借助getNode方法进行实现。 程序实现:

public boolean contains(K key) {

return getNode(key) != null;

}

public V get(K key) {

Node node = getNode(key);

return node == null ? null : node.value;

}

5.4、更改操作

更改操作也是借助getNode方法进行更改。

public void set(K key, V newValue) {

Node node = getNode(key);

if (node != null)

node.value = newValue;

else

throw new IllegalArgumentException(key + "doesn't exist");

}

最后

更多精彩内容,大家可以转到我的主页:源码地址在主页内哦~~首页 | 曲怪曲怪​quguai.cn

java avl_Java底层实现AVL 平衡二叉树相关推荐

  1. 数据结构中常见的树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)

    原文:http://blog.csdn.net/sup_heaven/article/details/39313731 数据结构中常见的树(BST二叉搜索树.AVL平衡二叉树.RBT红黑树.B-树.B ...

  2. java底层原理书籍_不愧是阿里p8大佬!终于把Java 虚拟机底层原理讲清楚了,请签收...

    概述 JVM 的内存模型和 JVM 的垃圾回收机制一直是 Java 业内从业者绕不开的话题(实际调优.面试)JVM是java中很重要的一块知识,也是面试常问的问题之一,直至今天,仍然还有许多面试者在被 ...

  3. 深入Java Thread底层源码实现

    深入Java Thread底层实现 介绍 Thread就是程序中一个线程的执行.JVM允许一个应用中多个线程并发执行 每个线程都有优先级.高优先级线程优先于低优先级线程执行 每个线程都可以(不可以)被 ...

  4. AVL平衡二叉树旋转详解

    AVL(平衡二叉树) AVL定义 左子树和右子树的高度之差的绝对值不超过1,高度之差也叫做平衡因子(Balance Factor),简称BF 左右子树也都是平衡二叉树 当一个节点没有左子树时,左子树高 ...

  5. java适合底层开发吗?相较于其他语言哪个好?

    我们都知道各种语言有各个的特点,长处不同,针对不同.向java本来就是主要用于互联网的,所以安全性要求很高,那么java适合底层开发吗?相较于其他语言哪个好?本篇帮大家解答一下此问题. java适合底 ...

  6. Java CAS底层原理

    Java CAS底层原理 Java CAS底层原理,这一篇就够了!!! CAS全称(Conmpare And Swap)比较并交换,是一种用于在多线程环境下实现同步功能的机制.CAS 操作包含三个操作 ...

  7. java数据结构与算法之平衡二叉树(AVL树)的设计与实现中的事实代码

    普通二叉查找树的问题   在开篇,我们提到过,普通二叉树(二叉查找树)在操作的时间复杂度上不一定遵循O(㏒n),也有可能是O(n),这是为什么呢?在上一篇中,我们明明插入都按照一定规则比较的呀,其实那 ...

  8. Java集合底层原理理解

    Java集合 List,Set,Map三者区别 List 顺序的好帮手:存储一组不唯一的有序的对象 Set 注重独一无二的性质:不允许重复的集合 Map 用key来搜索:使用键值对存储.两个key可以 ...

  9. java linkedlist底层_手写Java LinkedList核心源码

    上一章我们手写了ArrayList的核心源码,ArrayList底层是用了一个数组来保存数据,数组保存数据的优点就是查找效率高,但是删除效率特别低,最坏的情况下需要移动所有的元素.在查找需求比较重要的 ...

最新文章

  1. python爬虫之cookie方式自动登录巴比特网
  2. http http应用
  3. java memcached 存储对象_memcached—向memcached中保存Java实体需注意的问题
  4. Oracle Spatial 翻译第一漳
  5. Delphi XE2 之 FireMonkey 入门(26) - 数据绑定: TBindingsList: TBindExprItems
  6. mysql分布式如何实现原理_分布式通讯协议实现原理
  7. ssm项目直接加html文件,如何把ssm项目和vue项目部署在云服务器(上)
  8. IE下img多余5像素空白
  9. 教程 | Hadoop集群搭建和简单应用
  10. mysql基本命令大全_Django 学习笔记之 如何设置和操作 mysql 数据库
  11. (BookxNote Pro)Windows版Marginnote 3 阅读神器 自动生成脑图/思维导图
  12. 从Jensen不等式到Minkowski不等式
  13. 实现MySQL允许远程连接
  14. oracle查询语句转sql,将sql server查询语句转换为oracle查询语句[紧急]
  15. Grails 技巧 - View 篇
  16. sql服务器的响应时间,如何解决:[Sql Server]超时时间已到。在操作完成之前超时时间已过或服务器未响应。...
  17. 喵喵玩 v3.8.5
  18. sha加密算法介绍以及java实现sha加密算法
  19. xp计算机无法正常启动,修复XP系统无法正常启动进入不了计算机的解决办法
  20. pli测试50题题库_【马士基销售代表面试】性格测试+12分钟50道题。-看准网

热门文章

  1. CodeForces 375D Tree and Queries
  2. 『Scrapy』爬虫框架入门
  3. android servlet 登陆,Android Studio+Servlet+MySql实现登录注册
  4. xdoj 易碎的鸟蛋(鹰蛋实验)
  5. qt qgis linux,QGIS初识-Qt编译环境配置
  6. 计算机科学技术作文600,生活因科技而精彩作文600字
  7. e.V4p.C0/index.php,php-fpm进程在Kubernetes中接收SIGKILL信号
  8. python自动化之文件处理_Python自动化办公之Word批量转成自定义格式的Excel
  9. 谷歌pixel3axl开发者模式_谷歌 Android Q 和 iOS 12.3新测试版发布,看完心动了!
  10. 在oracle的安全策略中,Oracle数据库的安全策略(转)