数据结构树之AVL树(平衡二叉树)
一 什么是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树(平衡二叉树)的更多相关文章
- 数据结构与算法——AVL树类的C++实现
关于AVL树的简单介绍能够參考:数据结构与算法--AVL树简单介绍 关于二叉搜索树(也称为二叉查找树)能够參考:数据结构与算法--二叉查找树类的C++实现 AVL-tree是一个"加上了额外 ...
- Mysql为什么使用b+树,而不是b树、AVL树或红黑树?
首先,我们应该考虑一个问题,数据库在磁盘中是怎样存储的?(答案写在下一篇文章中) b树.b+树.AVL树.红黑树的区别很大.虽然都可以提高搜索性能,但是作用方式不同. 通常文件和数据库都存储在磁盘,如 ...
- 图解数据结构树之AVL树
AVL树(平衡二叉树): AVL树本质上是一颗二叉查找树,但是它又具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树.在AVL树中任何节点的两个子 ...
- 数据结构(三)实现AVL树
AVL树的定义 一种自平衡二叉查找树,中面向内存的数据结构. 二叉搜索树T为AVL树的满足条件为: T是空树 T若不是空树,则TL.TR都是AVL树,且|HL-HR| <= 1 (节点的左子树高 ...
- 数据结构实验7:实现二分查找、二叉排序(查找)树和AVL树
实验7 学号: 姓名: 专业: 7.1实验目的 (1) 掌握顺序表的查找方法,尤其是二分查找方法. (2) 掌握二叉排序树的建立及查找. 查找是软件设计中的最常用的运算,查找所涉及到 ...
- 数据结构与算法分析-AVL树
1.AVL树是带有平衡条件的二叉查找树. 2.AVL树的每个节点高度最多相差1. 3.AVL树实现的难点在于插入或删除操作.由于插入和删除都有可能破坏AVL树高度最多相差1的特性,所以当特性被破坏时需 ...
- 数据结构——二叉查找树、AVL树
二叉查找树:由于二叉查找树建树的过程即为插入的过程,所以其中序遍历一定为升序排列! 插入:直接插入,插入后一定为根节点 查找:直接查找 删除:叶子节点直接删除,有一个孩子的节点删除后将孩子节点接入到父 ...
- "《算法导论》之‘树’":AVL树
本文关于AVL树的介绍引自博文AVL树(二)之 C++的实现,与二叉查找树相同的部分则不作介绍直接引用:代码实现是在本文的基础上自己实现且继承自上一篇博文二叉查找树. 1.AVL树的介绍 AVL树是高 ...
- [数据结构与算法] : AVL树
头文件 typedef int ElementType; #ifndef _AVLTREE_H_ #define _AVLTREE_H_ struct AvlNode; typedef struct ...
随机推荐
- Java中static、final、static final的区别(转)
说明:不一定准确,但是最快理解. final: final可以修饰:属性,方法,类,局部变量(方法中的变量) final修饰的属性的初始化可以在编译期,也可以在运行期,初始化后不能被改变. final ...
- extentreports
关于extentreports使用的一些个人见解 首先导入jar包, 使用maven导入,我再次首先导入的是 <version>4.0.5</version>版本的jar包,但 ...
- eclipse连接mysql数据库
我这里在eclipse新建一个maven 项目做测试 首先我们要在本地电脑安装了mysql数据库和mysql驱动包 我的mysql数据库是通过phpstudy自带的 这个是驱动包 window–> ...
- (最完美)红米手机4的USB调试模式在哪里开启的经验
每次我们使用安卓手机通过数据线连接上Pc的时候,或者使用的有些应用比如我们团队营销部门每次使用的应用引号精灵,之前的老版本就需要开启Usb开发者调试模式下使用,现每次新版本不需要了,如果手机没有开启U ...
- include和require的区别
include与require除了在处理引入文件的方式不同外,最大的区别就是:include在引入不存文件时产生一个警告且脚本还会继续执行,require则会导致一个致命性错误且脚本停止执行. inc ...
- TreeMap中文排序,TreeMap倒序输出排列
1.TreeMap集合倒序排列 import java.util.Comparator; /** * 比较算法的类,比较器 * @author Administrator * */ public cl ...
- Intellij IDEA快捷键大全汇总(2019更新)
Intellij IDEA快捷键大全汇总(2019) Ctrl+Shift + Enter,语句完成“!”,否定完成,输入表达式时按 “!”键 Alt+回车 导入包,自动修正 Ctrl+N 查找类 ...
- IntelliJ IDEA SVN突然没有了
1.在IDEA中找不到 SVN 的选项了,版本控制工具中没有subversion,在setting中也无法查询到对应的svn工具,这是因为我们在idea的svn插件中把svn这个选项禁用了 解决办法: ...
- 分布式 基本理论 CAP
谈及分布式,必然谈到 CAP, CAP 已经是被说烂了的一个 话题, 绕不开, 逃不掉. 而且, 理解起来 会有些吃力. 分布式的CAP理论告诉我们 “任何一个分布式系统都无法同时满足 一致性(Co ...
- oracle 的tnsnames.ora,listener.ora
x:\app\Administrator\product\11.2.0\dbhome_1\NETWORK\ADMIN listener.ora: # listener.ora Network Conf ...