Python与数据结构[3] -> 树/Tree[2] -> AVL 平衡树和树旋转的 Python 实现
AVL 平衡树和树旋转
目录
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 实现的更多相关文章
- Python与数据结构[1] -> 栈/Stack[0] -> 链表栈与数组栈的 Python 实现
栈 / Stack 目录 链表栈 数组栈 栈是一种基本的线性数据结构(先入后出FILO),在 C 语言中有链表和数组两种实现方式,下面用 Python 对这两种栈进行实现. 1 链表栈 链表栈是以单链 ...
- Python -- 堆数据结构 heapq - I love this game! - 博客频道 - CSDN.NET
Python -- 堆数据结构 heapq - I love this game! - 博客频道 - CSDN.NET Python -- 堆数据结构 heapq 分类: Python 2012-09 ...
- (python数据分析)第03章 Python的数据结构、函数和文件
本章讨论Python的内置功能,这些功能本书会用到很多.虽然扩展库,比如pandas和Numpy,使处理大数据集很方便,但它们是和Python的内置数据处理工具一同使用的. 我们会从Python最基础 ...
- Python与数据结构[3] -> 树/Tree[1] -> 表达式树和查找树的 Python 实现
表达式树和查找树的 Python 实现 目录 二叉表达式树 二叉查找树 1 二叉表达式树 表达式树是二叉树的一种应用,其树叶是常数或变量,而节点为操作符,构建表达式树的过程与后缀表达式的计算类似,只不 ...
- Python与数据结构[3] -> 树/Tree[0] -> 二叉树及遍历二叉树的 Python 实现
二叉树 / Binary Tree 二叉树是树结构的一种,但二叉树的每一个节点都最多只能有两个子节点. Binary Tree: 00 |_____ | | 00 00 |__ |__ | | | | ...
- 数据结构图文解析之:AVL树详解及C++模板实现
0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...
- 数据结构(一)二叉树 & avl树 & 红黑树 & B-树 & B+树 & B*树 & R树
参考文档: avl树:http://lib.csdn.net/article/datastructure/9204 avl树:http://blog.csdn.net/javazejian/artic ...
- 用python讲解数据结构之树的遍历
树的结构 树(tree)是一种抽象数据类型或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合 它具有以下的特点: ①每个节点有零个或多个子节点: ②没有父节点的节点称为根节点: ③ ...
- 树的平衡 AVL Tree
本篇随笔主要从以下三个方面介绍树的平衡: 1):BST不平衡问题 2):BST 旋转 3):AVL Tree 一:BST不平衡问题的解析 之前有提过普通BST的一些一些缺点,例如BST的高度是介于lg ...
随机推荐
- P1118 [USACO06FEB]数字三角形`Backward Digit Su`…
题目描述 FJ and his cows enjoy playing a mental game. They write down the numbers from 11 to N(1 \le N \ ...
- DataBase -- Second Highest Salary
Question: Write a SQL query to get the second highest salary from the Employee table. +----+-------- ...
- bootstrap-table 增加序号列(支持分页)
columns: [ { checkbox: true }, { title: '序号', align: 'center', halign: 'center', formatter: function ...
- 【题解】NOI2017游戏
2-SAT.洛谷P3845 一开始以为——怎么有3个呢?后来发现因为每个地图都有一种车是不能用的,所以就等于每一个地图都有两个适应的车啦. 那么对于x类型的地图呢——只有8个,直接2^8暴力枚举每一种 ...
- POJ 2398 Toy Storage 二分+叉积
Description Mom and dad have a problem: their child, Reza, never puts his toys away when he is finis ...
- hadoop之HDFS与MapReduce
Hadoop历史 雏形开始于2002年的Apache的Nutch,Nutch是一个开源Java 实现的搜索引擎.它提供了我们运行自己的搜索引擎所需的全部工具.包括全文搜索和Web爬虫. 随后在2003 ...
- Extend the size of ext3 partition online in VM
1. Increase disk space in vCenter 2. scan disk on the Linux VM # fdisk -lu > /tmp/fdisk. # > / ...
- C#与数据库的连接的三种方式
学习了.net的知识从C#一直到MVC,我一直觉得基础很重要,最近有复习一下数据库连接的三种方式 1 返回结果集的一张表 public static DataTable ExecuteDataTabl ...
- The 2017 ACM-ICPC Asia East Continent League Final记录
首先感谢tyz学弟的麻麻-给我们弄到了名额- 然后就开始了ACM ECLFinal的玩耍,A*仙人掌可是立了flag要好好打的- 试机赛好像就全是GCJ kickstart的原题,然后AK了但是由于一 ...
- Spring - IoC(4): p-namespace & c-namespace
p 命名空间 p 命名空间允许你使用 bean 元素的属性而不是 <property/>子元素来描述 Bean 实例的属性值.从 Spring2.0 开始,Spring 支持基于 XML ...