AVL 平衡树和树旋转


目录

  1. AVL平衡二叉树
  2. 树旋转
  3. 代码实现

1 AVL平衡二叉树

AVL(Adelson-Velskii & Landis)树是一种带有平衡条件的二叉树,一棵AVL树其实是一棵左子树和右子树高度最多差1的二叉查找树。一棵树的不平衡主要是由于插入和删除的过程中产生的,此时则需要使用旋转来对AVL树进行平衡。

AVL Tree:
0
_____|_____
| |
0 0
|___ ___|___
| | |
0 0 0
|__
|
0

插入引起不平衡主要有以下四种情况:

Insert unbalance:
1: | 2: | 3: | 4:
A(2) | A(2) | A(2) | A(2)
__| | __| | |__ | |__
| | | | | | |
B(1) | B(1) | B(1) | B(1)
__| | |__ | __| | |__
| | | | | | |
---- | ---- | ---- | ----
|X(0)| | |X(0)| | |X(0)| | |X(0)|
---- | ---- | ---- | ----

删除引起不平衡主要有以下四种情况:

Delete unbalance:
1: | 2: | 3: | 4:
A(2) | A(2) | A(2) | A(2)
__|__ | __|__ | __|__ | __|__
| | | | | | | | | | |
B(1) ---- | B(1) ---- | ---- B(1) | ---- B(1)
__| |X(0)| | |__ |X(0)| | |X(0)| __| | |X(0)| |__
| ---- | | ---- | ---- | | ---- |
C(0) | C(0) | C(0) | C(0)

2. 树旋转

对于上面的不平衡情况,可以分别采用以下的树旋转方式解决,

2.1 向左单旋转

适用于处理上面插入/删除引起的不平衡情况1,

下图中K2两个子树节点相差高度等于2,产生了不平衡,因此需要使用单旋转处理,

在向左单旋转时,若K1有右子树,则转变为K2的左子树。

                 K2(2)                      K1(1)
__|__ ____|_____
| | | |
K1(1) None(-1) --------> X(0) K2(1)
__|__ __|__
| | | |
X(0) Y(0) Y(0) None(-1)

2.2 向右单旋转

适用于处理上面插入/删除引起的不平衡情况4,

在向右单旋转时,若K1有左子树,则转变为K2的右子树。

                 K2(2)                      K1(2)
__|__ ____|_____
| | | |
(-1)None K1(1) --------> K2(1) Y(0)
__|__ __|__
| | | |
X(0) Y(0) (-1)None X(0)

2.3 右左双旋转

适用于处理上面插入/删除引起的不平衡情况2,

首先对K1进行一次向右单旋转,再对K3进行一次向左单旋转,

                 K3(3)                     K3(3)                     K2(2)
__|__ __|__ _____|_____
| | right | | left | |
K1(2) D(0) --------> K2(2) D(0) --------> K1(1) K3(1)
__|__ __|__ __|__ __|__
| | | | | | | |
A(0) K2(1) K1(1) C(0) A(0) B(0) C(0) D(0)
__|__ __|__
| | | |
B(0) C(0) A(0) B(0)

2.4 左右双旋转

适用于处理上面插入/删除引起的不平衡情况3,

首先对K1进行一次向左单旋转,再对K3进行一次向右单旋转,

                 K3(3)                     K3(3)                     K2(2)
__|__ __|__ _____|_____
| | left | | right | |
D(0) K1(2) --------> D(0) K2(2) --------> K3(1) K1(1)
__|__ __|__ __|__ __|__
| | | | | | | |
K2(1) A(0) C(0) K1(1) D(0) C(0) B(0) A(0)
__|__ __|__
| | | |
C(0) B(0) B(0) A(0)

3 Python代码实现

下面利用Python实现AVL树,以及树的旋转操作,

完整代码

 from search_tree import SearchTree

 class AvlNode:
def __init__(self, val=None, lef=None, rgt=None, hgt=0):
self.value = val
self.left = lef
self.right = rgt
self.height = hgt def __str__(self):
return str(self.value) class AvlTree(SearchTree):
"""
AVL Tree:
0
_____|_____
| |
0 0
|___ ___|___
| | |
0 0 0
|__
|
0
Insert unbalance:
1: | 2: | 3: | 4:
A(2) | A(2) | A(2) | A(2)
__| | __| | |__ | |__
| | | | | | |
B(1) | B(1) | B(1) | B(1)
__| | |__ | __| | |__
| | | | | | |
---- | ---- | ---- | ----
|X(0)| | |X(0)| | |X(0)| | |X(0)|
---- | ---- | ---- | ---- Delete unbalance:
1: | 2: | 3: | 4:
A(2) | A(2) | A(2) | A(2)
__|__ | __|__ | __|__ | __|__
| | | | | | | | | | |
B(1) ---- | B(1) ---- | ---- B(1) | ---- B(1)
__| |X(0)| | |__ |X(0)| | |X(0)| __| | |X(0)| |__
| ---- | | ---- | ---- | | ---- |
C(0) | C(0) | C(0) | C(0)
"""
def get_height(self, node):
if isinstance(node, AvlNode):
return node.height
return -1 def single_rotate_left(self, k2):
"""
K2(2) K1(1)
__|__ ____|_____
| | | |
K1(1) None(-1) --------> X(0) K2(1)
__|__ __|__
| | | |
X(0) Y(0) Y(0) None(-1)
"""
# Left rotate
k1 = k2.left
k2.left = k1.right
k1.right = k2
# Update height
k2.height = max(self.get_height(k2.left), self.get_height(k2.right)) + 1
k1.height = max(self.get_height(k1.left), k2.height) + 1
return k1 def single_rotate_right(self, k2):
"""
K2(2) K1(2)
__|__ ____|_____
| | | |
(-1)None K1(1) --------> K2(1) Y(0)
__|__ __|__
| | | |
X(0) Y(0) (-1)None X(0)
"""
# Right rotate
k1 = k2.right
k2.right = k1.left
k1.left = k2
# Update height
k2.height = max(self.get_height(k2.left), self.get_height(k2.right)) + 1
k1.height = max(k2.height, self.get_height(k1.right)) + 1
return k1 def double_rotate_left(self, k3):
"""
K3(3) K3(3) K2(2)
__|__ __|__ _____|_____
| | right | | left | |
K1(2) D(0) --------> K2(2) D(0) --------> K1(1) K3(1)
__|__ __|__ __|__ __|__
| | | | | | | |
A(0) K2(1) K1(1) C(0) A(0) B(0) C(0) D(0)
__|__ __|__
| | | |
B(0) C(0) A(0) B(0)
"""
# Rotate between k1 and k2
k3.left = self.single_rotate_right(k3.left)
# Rotate between k3 and k2
return self.single_rotate_left(k3) def double_rotate_right(self, k3):
"""
K3(3) K3(3) K2(2)
__|__ __|__ _____|_____
| | left | | right | |
D(0) K1(2) --------> D(0) K2(2) --------> K3(1) K1(1)
__|__ __|__ __|__ __|__
| | | | | | | |
K2(1) A(0) C(0) K1(1) D(0) C(0) B(0) A(0)
__|__ __|__
| | | |
C(0) B(0) B(0) A(0)
"""
# Rotate between k1 and k2
k3.right = self.single_rotate_left(k3.right)
# Rotate between k3 and k2
return self.single_rotate_right(k3) def insert(self, item):
if self._root is None:
self._root = AvlNode(item)
return def _insert(item, node):
if not node:
return AvlNode(item)
if item < node.value:
node.left = _insert(item, node.left)
if self.get_height(node.left) - self.get_height(node.right) == 2:
if item < node.left.value: # Situation 1: left --> left
node = self.single_rotate_left(node)
else: # Situation 2: left --> right
node = self.double_rotate_left(node)
elif item > node.value:
node.right = _insert(item, node.right)
if self.get_height(node.right) - self.get_height(node.left) == 2:
if item < node.right.value: # Situation 3: right --> left
node = self.double_rotate_right(node)
else: # Situation 4: right --> right
node = self.single_rotate_right(node)
else: pass
# Update node height (if not rotated, update is necessary.)
node.height = max(self.get_height(node.left), self.get_height(node.right)) + 1
return node
self._root = _insert(item, self._root) def delete(self, item):
if self._root is None:
return def _delete(item, node):
if not node: # Node no found
# return None
raise Exception('Element not in tree.') if item < node.value:
node.left = _delete(item, node.left)
# Delete left, rotate right
if self.get_height(node.right) - self.get_height(node.left) == 2:
if self.get_height(node.right.right) >= self.get_height(node.right.left): # Situation 4
node = self.single_rotate_right(node)
else: # Situation 3
node = self.double_rotate_right(node) elif item > node.value:
node.right = _delete(item, node.right)
# Delete right, rotate left
if self.get_height(node.left) - self.get_height(node.right) == 2:
if self.get_height(node.left.left) >= self.get_height(node.left.right): # Situation 1
node = self.single_rotate_left(node)
else: # Situation 3
node = self.double_rotate_left(node) else: # Node found
if node.left and node.right:
# Minimum node in right sub-tree has no left sub-node, can be used to make replacement
# Find minimum node in right sub-tree
min_node = self.find_min(node.right)
# Replace current node with min_node
node.value = min_node.value
# Delete min_node in right sub-tree
node.right = _delete(min_node.value, node.right)
else:
if node.left:
node = node.left
elif node.right:
node = node.right
else:
node = None
# Update node height (if not ratated, update is necessary.)
# If node is None, height is -1 already.
if node:
node.height = max(self.get_height(node.left), self.get_height(node.right)) + 1
return node
self._root = _delete(item, self._root) def test(avl):
print('\nInit an AVL tree:')
for i in [1, 2, 3, 4, 5, 6, 7]:
avl.insert(i)
avl.show() print('\nLeft single rotate:')
avl.delete(5)
avl.delete(7)
avl.delete(6)
avl.show() avl.make_empty()
print('\nInit an AVL tree:')
for i in [1, 2, 3, 4, 5, 6, 7]:
avl.insert(i) print('\nRight single rotate:')
avl.delete(1)
avl.delete(3)
avl.delete(2)
avl.show() avl.make_empty()
print('\nInit an AVL tree:')
for i in [6, 2, 8, 1, 4, 9, 3, 5]:
avl.insert(i)
avl.show() print('\nRight-Left double rotate:')
avl.delete(9)
avl.show() avl.make_empty()
print('\nInit an AVL tree:')
for i in [4, 2, 8, 1, 6, 9, 5, 7]:
avl.insert(i)
avl.show() print('\nLeft-Right double rotate:')
avl.delete(1)
avl.show() if __name__ == '__main__':
test(AvlTree())

分段解释

首先,需要导入查找树,以及定义AVL树的节点,

 from search_tree import SearchTree

 class AvlNode:
def __init__(self, val=None, lef=None, rgt=None, hgt=0):
self.value = val
self.left = lef
self.right = rgt
self.height = hgt def __str__(self):
return str(self.value)

接着构建一棵AVL树的类,构造方法可继承查找树,

 class AvlTree(SearchTree):
"""
AVL Tree:
0
_____|_____
| |
0 0
|___ ___|___
| | |
0 0 0
|__
|
0
Insert unbalance:
1: | 2: | 3: | 4:
A(2) | A(2) | A(2) | A(2)
__| | __| | |__ | |__
| | | | | | |
B(1) | B(1) | B(1) | B(1)
__| | |__ | __| | |__
| | | | | | |
---- | ---- | ---- | ----
|X(0)| | |X(0)| | |X(0)| | |X(0)|
---- | ---- | ---- | ---- Delete unbalance:
1: | 2: | 3: | 4:
A(2) | A(2) | A(2) | A(2)
__|__ | __|__ | __|__ | __|__
| | | | | | | | | | |
B(1) ---- | B(1) ---- | ---- B(1) | ---- B(1)
__| |X(0)| | |__ |X(0)| | |X(0)| __| | |X(0)| |__
| ---- | | ---- | ---- | | ---- |
C(0) | C(0) | C(0) | C(0)
"""

定义获取节点子树高度的方法,当节点为None时,返回高度为-1,

     def get_height(self, node):
if isinstance(node, AvlNode):
return node.height
return -1

定义向左的单旋转方法,旋转完成后更新高度

     def single_rotate_left(self, k2):
"""
K2(2) K1(1)
__|__ ____|_____
| | | |
K1(1) None(-1) --------> X(0) K2(1)
__|__ __|__
| | | |
X(0) Y(0) Y(0) None(-1)
"""
# Left rotate
k1 = k2.left
k2.left = k1.right
k1.right = k2
# Update height
k2.height = max(self.get_height(k2.left), self.get_height(k2.right)) + 1
k1.height = max(self.get_height(k1.left), k2.height) + 1
return k1

定义向右的单旋转方法,旋转完成后更新高度

     def single_rotate_right(self, k2):
"""
K2(2) K1(2)
__|__ ____|_____
| | | |
(-1)None K1(1) --------> K2(1) Y(0)
__|__ __|__
| | | |
X(0) Y(0) (-1)None X(0)
"""
# Right rotate
k1 = k2.right
k2.right = k1.left
k1.left = k2
# Update height
k2.height = max(self.get_height(k2.left), self.get_height(k2.right)) + 1
k1.height = max(k2.height, self.get_height(k1.right)) + 1
return k1

定义先右后左的双旋转方法,旋转完成后更新高度

     def double_rotate_left(self, k3):
"""
K3(3) K3(3) K2(2)
__|__ __|__ _____|_____
| | right | | left | |
K1(2) D(0) --------> K2(2) D(0) --------> K1(1) K3(1)
__|__ __|__ __|__ __|__
| | | | | | | |
A(0) K2(1) K1(1) C(0) A(0) B(0) C(0) D(0)
__|__ __|__
| | | |
B(0) C(0) A(0) B(0)
"""
# Rotate between k1 and k2
k3.left = self.single_rotate_right(k3.left)
# Rotate between k3 and k2
return self.single_rotate_left(k3)

定义先左后右的双旋转方法,旋转完成后更新高度

     def double_rotate_right(self, k3):
"""
K3(3) K3(3) K2(2)
__|__ __|__ _____|_____
| | left | | right | |
D(0) K1(2) --------> D(0) K2(2) --------> K3(1) K1(1)
__|__ __|__ __|__ __|__
| | | | | | | |
K2(1) A(0) C(0) K1(1) D(0) C(0) B(0) A(0)
__|__ __|__
| | | |
C(0) B(0) B(0) A(0)
"""
# Rotate between k1 and k2
k3.right = self.single_rotate_left(k3.right)
# Rotate between k3 and k2
return self.single_rotate_right(k3)

定义插入方法,进行递归插入,每次插入后都需要检测子树高度来决定是否需要对树进行平衡旋转,最后进行高度更新,若经过旋转则此时高度已经被更新。

     def insert(self, item):
if self._root is None:
self._root = AvlNode(item)
return def _insert(item, node):
if not node:
return AvlNode(item)
if item < node.value:
node.left = _insert(item, node.left)
if self.get_height(node.left) - self.get_height(node.right) == 2:
if item < node.left.value: # Situation 1: left --> left
node = self.single_rotate_left(node)
else: # Situation 2: left --> right
node = self.double_rotate_left(node)
elif item > node.value:
node.right = _insert(item, node.right)
if self.get_height(node.right) - self.get_height(node.left) == 2:
if item < node.right.value: # Situation 3: right --> left
node = self.double_rotate_right(node)
else: # Situation 4: right --> right
node = self.single_rotate_right(node)
else: pass
# Update node height (if not rotated, update is necessary.)
node.height = max(self.get_height(node.left), self.get_height(node.right)) + 1
return node
self._root = _insert(item, self._root)

定义删除方法,进行递归删除,删除后检测是否需要平衡旋转,并选择旋转的方式,删除完成根据需要进行高度更新。

     def delete(self, item):
if self._root is None:
return def _delete(item, node):
if not node: # Node no found
# return None
raise Exception('Element not in tree.') if item < node.value:
node.left = _delete(item, node.left)
# Delete left, rotate right
if self.get_height(node.right) - self.get_height(node.left) == 2:
if self.get_height(node.right.right) >= self.get_height(node.right.left): # Situation 4
node = self.single_rotate_right(node)
else: # Situation 3
node = self.double_rotate_right(node) elif item > node.value:
node.right = _delete(item, node.right)
# Delete right, rotate left
if self.get_height(node.left) - self.get_height(node.right) == 2:
if self.get_height(node.left.left) >= self.get_height(node.left.right): # Situation 1
node = self.single_rotate_left(node)
else: # Situation 3
node = self.double_rotate_left(node) else: # Node found
if node.left and node.right:
# Minimum node in right sub-tree has no left sub-node, can be used to make replacement
# Find minimum node in right sub-tree
min_node = self.find_min(node.right)
# Replace current node with min_node
node.value = min_node.value
# Delete min_node in right sub-tree
node.right = _delete(min_node.value, node.right)
else:
if node.left:
node = node.left
elif node.right:
node = node.right
else:
node = None
# Update node height (if not ratated, update is necessary.)
# If node is None, height is -1 already.
if node:
node.height = max(self.get_height(node.left), self.get_height(node.right)) + 1
return node
self._root = _delete(item, self._root)

最后定义一个测试函数,用于测试AVL树的功能。

首先初始化一棵树

 def test(avl):
print('\nInit an AVL tree:')
for i in [1, 2, 3, 4, 5, 6, 7]:
avl.insert(i)
avl.show()

得到结果

Init an AVL tree:
4 | 4
2 | __|__
1 | | |
3 | 2 6
6 | _|_ _|_
5 | | | | |
7 | 1 3 5 7

接着尝试删除5, 6, 7三个节点,完成一个向左单旋转,

     print('\nLeft single rotate:')
avl.delete(5)
avl.delete(7)
avl.delete(6)
avl.show()

得到结果,可以看到,此时AVL树已经完成了一个向左的平衡旋转

Left single rotate:
2 | 2
1 | __|__
4 | | |
3 | 1 4
| _|
| |
| 3

清空树并再次初始化树后

尝试删除1, 2, 3三个节点,完成一个向右单旋转,

     print('\nDelete element from tree:')
avl.delete(1)
avl.delete(3)
avl.delete(2)
avl.show()

得到结果,可以看到,此时AVL树已经完成了一个向右的平衡旋转

Delete element from tree:
6 | 6
4 | __|__
5 | | |
7 | 4 7
| |_
| |
| 5

再次清空树,并建立一棵新的树

     avl.make_empty()
print('\nInit an AVL tree:')
for i in [6, 2, 8, 1, 4, 9, 3, 5]:
avl.insert(i)
avl.show()

得到结果

Init an AVL tree:
6 | 6
2 | __|__
1 | | |
4 | 2 8
3 | _|_ |_
5 | | | |
8 | 1 4 9
9 | _|_
| | |
| 3 5

此时尝试删除节点9,完成一个右左双旋转

     print('\nRight-Left double rotate:')
avl.delete(9)
avl.show()

得到结果

Right-Left double rotate:
4 | 4
2 | __|__
1 | | |
3 | 2 6
6 | _|_ _|_
5 | | | | |
8 | 1 3 5 8

最后再次清空树建立一棵新树,

     avl.make_empty()
print('\nInit an AVL tree:')
for i in [4, 2, 8, 1, 6, 9, 5, 7]:
avl.insert(i)
avl.show()

显示结果如下

Init an AVL tree:
4 | 4
2 | __|__
1 | | |
8 | 2 8
6 | _| _|_
5 | | | |
7 | 1 6 9
9 | _|_
| | |
| 5 7

此时尝试删除节点1,造成不平衡完成一个左右双旋转

     print('\nLeft-Right double rotate:')
avl.delete(1)
avl.show()

得到结果

Left-Right double rotate:
6 | 6
4 | __|__
2 | | |
5 | 4 8
8 | _|_ _|_
7 | | | | |
9 | 2 5 7 9

相关阅读


1. 二叉树

2. 查找树

Python与数据结构[3] -> 树/Tree[2] -> AVL 平衡树和树旋转的 Python 实现的更多相关文章

  1. Python与数据结构[1] -> 栈/Stack[0] -> 链表栈与数组栈的 Python 实现

    栈 / Stack 目录 链表栈 数组栈 栈是一种基本的线性数据结构(先入后出FILO),在 C 语言中有链表和数组两种实现方式,下面用 Python 对这两种栈进行实现. 1 链表栈 链表栈是以单链 ...

  2. Python -- 堆数据结构 heapq - I love this game! - 博客频道 - CSDN.NET

    Python -- 堆数据结构 heapq - I love this game! - 博客频道 - CSDN.NET Python -- 堆数据结构 heapq 分类: Python 2012-09 ...

  3. (python数据分析)第03章 Python的数据结构、函数和文件

    本章讨论Python的内置功能,这些功能本书会用到很多.虽然扩展库,比如pandas和Numpy,使处理大数据集很方便,但它们是和Python的内置数据处理工具一同使用的. 我们会从Python最基础 ...

  4. Python与数据结构[3] -> 树/Tree[1] -> 表达式树和查找树的 Python 实现

    表达式树和查找树的 Python 实现 目录 二叉表达式树 二叉查找树 1 二叉表达式树 表达式树是二叉树的一种应用,其树叶是常数或变量,而节点为操作符,构建表达式树的过程与后缀表达式的计算类似,只不 ...

  5. Python与数据结构[3] -> 树/Tree[0] -> 二叉树及遍历二叉树的 Python 实现

    二叉树 / Binary Tree 二叉树是树结构的一种,但二叉树的每一个节点都最多只能有两个子节点. Binary Tree: 00 |_____ | | 00 00 |__ |__ | | | | ...

  6. 数据结构图文解析之:AVL树详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  7. 数据结构(一)二叉树 & avl树 & 红黑树 & B-树 & B+树 & B*树 & R树

    参考文档: avl树:http://lib.csdn.net/article/datastructure/9204 avl树:http://blog.csdn.net/javazejian/artic ...

  8. 用python讲解数据结构之树的遍历

    树的结构 树(tree)是一种抽象数据类型或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合 它具有以下的特点: ①每个节点有零个或多个子节点: ②没有父节点的节点称为根节点: ③ ...

  9. 树的平衡 AVL Tree

    本篇随笔主要从以下三个方面介绍树的平衡: 1):BST不平衡问题 2):BST 旋转 3):AVL Tree 一:BST不平衡问题的解析 之前有提过普通BST的一些一些缺点,例如BST的高度是介于lg ...

随机推荐

  1. PowerShell收发TCP消息包

    PowerShell收发TCP消息包 https://www.cnblogs.com/fuhj02/archive/2012/10/16/2725609.html 在上篇文章中,我们在PSNet包中创 ...

  2. [BZOJ1283]序列

    Description 给出一个长度为n的正整数序列Ci,求一个子序列,使得原序列中任意长度为m的子串中被选出的元素不超过K(K,M<=100) 个,并且选出的元素之和最大. Input 第1行 ...

  3. 某ISP的流氓行径 劫持用户HTTP请求插入js代码

    最近公司搞的项目有用户反应点击任意链接后偶尔会跳到一个“莫名奇妙”的网站………… 喏,就是这个咯.

  4. [Leetcode] 3sum 三数和

    Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all un ...

  5. Codeforces Round #520 (Div. 2) B. Math

    B. Math time limit per test:1 second memory limit per test:256 megabytes Description: JATC's math te ...

  6. java禁止实例化的工具类

    public class Q { /** * @param args */ public static void main(String[] args) { new Person() } } clas ...

  7. 两个数组的交集 II [ LeetCode - 350 ]

      原题地址:https://leetcode-cn.com/problems/intersection-of-two-arrays-ii/description/   给定两个数组,写一个方法来计算 ...

  8. Tumblr:150亿月浏览量背后的架构挑战

    Tumblr:150亿月浏览量背后的架构挑战 2013/04/08 · IT技术, 开发 · 9.9K 阅读 · HBase, Tumblr, 架构 英文原文:High Scalability,编译: ...

  9. 学习python类

    类:Python 类提供了面向对象编程的所有基本特征: 允许多继承的类继承机制, 派生类可以重写它父类的任何方法, 一个方法可以调用父类中重名的方法. 对象可以包含任意数量和类型的数据成员. 作为模块 ...

  10. Spring学习--切面优先级及重用切点表达式

    指定切面的优先级: 在同一个链接点上应用不止一个切面时 , 除非明确指定 , 否则它们的优先级是不确定的. 切面的优先级可以通过实现 Ordered 接口或利用 @Order 注解指定. 实现 Ord ...