一 什么是AVL树(平衡二叉树):

AVL树本质上是一颗二叉查找树,但是它又具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为平衡二叉树。下面是平衡二叉树和非平衡二叉树对比的例图:

平衡因子(bf):结点的左子树的深度减去右子树的深度,那么显然-1<=bf<=1;

AVL树具有以下性质:

  • 根的左右子树的高度之差的绝对值不能超过1
  • 根的左右子树都是平衡二叉树

二 AVL树的旋转

插入一个节点可能会破坏AVL树的平衡, 可以通过旋转操作来进行修正
插入一个节点后,只有从插入节点到根节点的路径上的节点的平衡可能被改变。我们需要找出第一个破坏了平衡条件的节点,称之为K。K的两颗子树高度相差2
不平衡的出可能有4种情况:

  • 不平衡是由于对k的右孩子的右子树插入导致的:左旋
  • 不平衡是由于对k的左孩子的左子树插入导致的:右旋
  • 不平衡是由于对k的右孩子的左子树插入导致的:右旋-左旋
  • 不平衡是由于对k的左孩子的右子树插入导致的:左旋-右旋

1 左旋

我们在进行节点插入的时候,可能会出现节点都倾向于左边的情况,例如:

这个时候,我们就可以对节点9进行右旋操作,使它恢复平衡。

即:顺时针旋转两个节点,使得父节点被自己的左孩子取代,而自己成为自己的右孩子

再举个例子:

节点4和9高度相差大于1。由于是左孩子的高度较高,此时是左-左型,进行右旋。

2 左旋

左旋和右旋一样,就是用来解决当大部分节点都偏向右边的时候,通过左旋来还原。例如:

3 右旋左旋

对于图中画圈部分

单单一次左旋或右旋是不行的,下面我们先说说如何处理这种情况。

处理的方法是先对节点10进行右旋把它变成右-右型。

然后在进行左旋。

调整之后:

4 左旋右旋

同理,也存在左-右型的,例如:

对于左-右型的情况和刚才的 右-左型相反,我们需要对它进行一次左旋,再右旋。

在插入的过程中,会出现一下四种情况破坏AVL树的特性,我们可以采取如下相应的旋转。

1、左-左型:做右旋。

2、右-右型:做左旋转。

3、左-右型:先做左旋,后做右旋。

4、右-左型:先做右旋,再做左旋。

三 AVL树的实现代码

 class AVLNode(object):
def __init__(self, data):
'''
AVL树的每个节点
''' self.data = data
self.lchild = None
self.rchild = None
self.parent = None
self.bf = 0 class AVLTree(object):
'''
AVL树相关操作
''' def __init__(self, li=None):
self.root = None
if li:
for val in li:
self.insert_no_rec(val) def pre_order(self, root):
if root:
print(root.data, end=",")
self.pre_order(root.lchild)
self.pre_order(root.rchild) def in_order(self, root):
if root:
self.in_order(root.lchild)
print(root.data, end=',')
self.in_order(root.rchild) def rotate_left(self, p, c):
s2 = c.lchild
p.rchild = s2
if s2:
s2.parent = p
c.lchild = p
p.parent = c
p.bf = 0
c.bf = 0
return c def rotate_right(self, p, c):
s2 = c.rchild
p.lchild = s2
if s2:
s2.parent = p
c.rchild = p
p.parent = c
p.bf = 0
c.bf = 0
return c def rotate_right_left(self, p, c):
g = c.lchild
s3 = g.rchild
c.lchild = s3
if s3:
s3.parent = c
g.rchild = c
c.parent = g s2 = g.lchild
p.rchild = s2
if s2:
s2.parent = p
g.lchild = p
p.parent = g # 更新bf
if g.bf > 0:
p.bf = -1
c.bf = 0
elif g.bf < 0:
p.bf = 0
c.bf = 1
else: # 插入的是g
p.bf = 0
c.bf = 0
return g def rotate_left_right(self, p, c):
g = c.rchild
s2 = g.lchild
c.rchild = s2
if s2:
s2.parent = c
g.lchild = c
c.parent = g s3 = g.rchild
p.lchild = s3
if s3:
s3.parent = p
g.rchild = p
p.parent = g # 更新bf
if g.bf < 0:
p.bf = 1
c.bf = 0
elif g.bf > 0:
p.bf = 0
c.bf = -1
else:
p.bf = 0
c.bf = 0
return g def insert_no_rec(self, val):
# 1 和BST一样插入
p = self.root
if not p: # 空树
self.root = AVLNode(val)
return
while True:
if val < p.data:
if p.lchild:
p = p.lchild
else: # 左子树不存在直接插入
p.lchild = AVLNode(val)
p.lchild.parent = p
node = p.lchild # node存储就是插入的节点
break
elif val > p.data:
if p.rchild:
p = p.rchild
else:
p.rchild = AVLNode(val)
p.rchild.parent = p
node = p.rchild
break
else: # val == p.data 一颗树如果插入同样的元素 不操作
return
# 更新balance factor
while node.parent: # node的parent不空
if node.parent.lchild == node: # 传递是从左子树来的, 左子树更沉了
# 更新node.parent的bf -=1
if node.parent.bf < 0: # 原来node.parent.bf == -1, 更新后变成-2
# 看node哪边沉
g = node.parent.parent # 为了连接旋转之后的子树
x = node.parent # 旋转之前子树的根
if node.bf > 0:
n = self.rotate_left_right(node.parent, node)
else:
n = self.rotate_right(node.parent, node)
elif node.parent.bf > 0: # 原来node.parent.bf=1 更新之后变成0
node.parent.bf = 0
break
else: # 原来node.parent.bf=0 更新之后变成-1
node.parent.bf = -1
node = node.parent
continue
else: # 传递是从右子树来的, 右子树更沉了
# 更新node.parent.bf += 1
if node.parent.bf > 0: # 原来node.parent.bf ==1, 更新后变成2
# 做旋转
# 看node那边沉
g = node.parent.parent # 为了连接旋转之后的子树
x = node.parent # 旋转之前子树的根
if node.bf < 0: # node.bf = 1
n = self.rotate_left_right(node.parent, node)
else: # node.bf = -1
n = self.rotate_left(node.parent, node) elif node.parent.bf < 0: # 原来node.parent.bf = -1 更新后变成0
node.parent.bf = 0
break
else: # 原来node.parent.bf =0 更新之后变成1
node.parent.bf = 1
node = node.parent
continue
# 链接旋转后的子树
n.parent = g
if g: # g不是空
if x == g.lchild:
g.lchild = n
else:
g.rchild = n
break
else:
self.root = n
break tree = AVLTree([9, 8, 7, 6, 5, 4, 3, 2, 1])
tree.pre_order(tree.root)
print("")
tree.in_order(tree.root)

数据结构树之AVL树(平衡二叉树)的更多相关文章

  1. 数据结构与算法——AVL树类的C++实现

    关于AVL树的简单介绍能够參考:数据结构与算法--AVL树简单介绍 关于二叉搜索树(也称为二叉查找树)能够參考:数据结构与算法--二叉查找树类的C++实现 AVL-tree是一个"加上了额外 ...

  2. Mysql为什么使用b+树,而不是b树、AVL树或红黑树?

    首先,我们应该考虑一个问题,数据库在磁盘中是怎样存储的?(答案写在下一篇文章中) b树.b+树.AVL树.红黑树的区别很大.虽然都可以提高搜索性能,但是作用方式不同. 通常文件和数据库都存储在磁盘,如 ...

  3. 图解数据结构树之AVL树

    AVL树(平衡二叉树): AVL树本质上是一颗二叉查找树,但是它又具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树.在AVL树中任何节点的两个子 ...

  4. 数据结构(三)实现AVL树

    AVL树的定义 一种自平衡二叉查找树,中面向内存的数据结构. 二叉搜索树T为AVL树的满足条件为: T是空树 T若不是空树,则TL.TR都是AVL树,且|HL-HR| <= 1 (节点的左子树高 ...

  5. 数据结构实验7:实现二分查找、二叉排序(查找)树和AVL树

    实验7 学号:      姓名:     专业: 7.1实验目的 (1) 掌握顺序表的查找方法,尤其是二分查找方法. (2) 掌握二叉排序树的建立及查找. 查找是软件设计中的最常用的运算,查找所涉及到 ...

  6. 数据结构与算法分析-AVL树

    1.AVL树是带有平衡条件的二叉查找树. 2.AVL树的每个节点高度最多相差1. 3.AVL树实现的难点在于插入或删除操作.由于插入和删除都有可能破坏AVL树高度最多相差1的特性,所以当特性被破坏时需 ...

  7. 数据结构——二叉查找树、AVL树

    二叉查找树:由于二叉查找树建树的过程即为插入的过程,所以其中序遍历一定为升序排列! 插入:直接插入,插入后一定为根节点 查找:直接查找 删除:叶子节点直接删除,有一个孩子的节点删除后将孩子节点接入到父 ...

  8. "《算法导论》之‘树’":AVL树

    本文关于AVL树的介绍引自博文AVL树(二)之 C++的实现,与二叉查找树相同的部分则不作介绍直接引用:代码实现是在本文的基础上自己实现且继承自上一篇博文二叉查找树. 1.AVL树的介绍 AVL树是高 ...

  9. [数据结构与算法] : AVL树

    头文件 typedef int ElementType; #ifndef _AVLTREE_H_ #define _AVLTREE_H_ struct AvlNode; typedef struct ...

随机推荐

  1. 神经网络常用的Numpy功能笔记

    数组初始化 x=np.array([[1,2]]) x=np.zeros((2,3)) 生成随机数 w=np.random.randn(2,3) PIL image转换成array img = np. ...

  2. Vue ElementUI 的使用

    简介: 饿了么公司基于vue开的的vue的Ui组件库        1.Element Ui    基于vue  pc端的UI框架          2. MintUi         基于vue 移 ...

  3. sql优化使用技巧

    1.LIMIT 语句分页查询是最常用的场景之一,但也通常也是最容易出问题的地方.比如对于下面简单的语句,一般 DBA 想到的办法是在 type, name, create_time 字段上加组合索引. ...

  4. 检测mysq组复制的脚本

    #!/bin/bash PROG_OUT="/root/checkmysql-group.log" # 输出日志到至此文件,建议配置绝对路径 #PROG_OUT="/de ...

  5. java基础语法(标识符 修饰符 关键字)

    标识符 用来表示类名,变量名,方法名,类型名,数组名,文件名的有效字符序列称为标识符.   1)只有字母(区分大小写),下划线,美元符号和数字组成,长度不受限制.注:字母包括英文26个字母 ,汉字,日 ...

  6. TIMESTAMP(6)类型的时间差

    TIMESTAMP 数据类型 它包括了所有DATE数据类型的年月日时分秒的信息,而且包括了小数秒的信息. 以分钟为单位查询时间差 select ROUND(TO_NUMBER(to_date(to_c ...

  7. --- rk3399/3288 系列平台接mipi 的dts 数据 panel-init-sequence = [] 命令的整法

    https://blog.csdn.net/Shushan1/article/details/87858434 mipi 屏的数据手册 dts sample: &dsi { status = ...

  8. SQL中IF和CASE语句

    IF表达式 IF(A,B,C): 如果 A 是TRUE (A <> 0 and A<> NULL),则 IF()的返回值为B; 否则返回值则为 C.IF() 的返回值为数字值或 ...

  9. 把一个syn报文给rst掉

    下面展示一个极其简单的例子,看如何使用netfilter来将一个指定端口的syn报文给rst掉. //************************************************* ...

  10. DOM访问和处理HTML文档的标准方法

    innerHTML获取或替换HTML元素的内容 <p id="d">对于错我无法来判断 </p><script type="text/jav ...