一 什么是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. 如何将composer设置为全局变量?

    全局安装是将 Composer 安装到系统环境变量 PATH 所包含的路径下面,然后就能够在命令行窗口中直接执行 composer 命令了. Mac 或 Linux 系统: 打开命令行窗口并执行如下命 ...

  2. Git命令行入门

    安装 下载与文档地址:https://git-scm.com/book/zh/v2 我使用的是linux系统,故使用命令行安装Git # apt-get install git 配置 # git co ...

  3. python 中list的深拷贝和浅拷贝

    注意:这里提到是list的隐式转换例如 a=[1,2,3]def test(n):n[2] = n[2]*2c=a此时是浅拷贝,当调用test(c)时,可以看到a也发生了变化c=[1,4,3]a=[1 ...

  4. vue+axios完美实现前端路由拦截

    一.路由拦截 1.首先在router的index.js里配置一个自定义字段requireAuth,用该字段来判断进入该路由是否需要登录.如果已经登陆则进入该路由,反之则进入登录页面. 如图是路由配置: ...

  5. react基础学习 二——生命周期

    生命周期mount: mounting装载创建 update更新 unmounting卸载 错误捕获 注意点:生命周期函数的 作用,什么之后用 只有类式组件有生命周期,函数式组件没有生命周期 moun ...

  6. linux网编 静态链接库

    -L 指定动态库路径 -l 指定 以libXXXX.a命名的库文件

  7. rownum用法

    对于rownum来说它是oracle系统顺序分配为从查询返回的行的编号,返回的第一行分配的是1,第二行是2,依此类推,这个伪字段可以用于限制查询返回的总行数,且rownum不能以任何表的名称作为前缀. ...

  8. 电脑修改密码后git上传失败Authentication failed

    (一)进入控制面板 (二)选择用户账户 (三)选择管理你的凭据 (四)选择Windows凭据 (五)选择git保存的用户信息 (六)选择编辑或者进行删除操作 (七)完成

  9. Centos7的防火墙关闭

    第一步.centos7安装service 第二步. 或者可以不用service,有另一个办法.

  10. 统计不同渠道的的UV,再合计渠道总计UV

    ( SELECT `统计时间`, `贷款公司名称`, `推广员姓名`, UV FROM ac_statistics WHERE `贷款公司名称` = "金腰袋" AND `统计时间 ...