AVL树的python实现
介绍
在计算机科学中,AVL树是最早被发明的自平衡二叉查找树。在AVL树中,任一节点对应的两棵子树的最大高度差为1,因此它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下的时间复杂度都是O(log n)。增加和删除元素的操作则可能需要借由一次或多次树旋转,以实现树的重新平衡。
节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。
特点
AVL树本质上还是一棵二叉搜索树,它的特点是:
1.本身首先是一棵二叉搜索树。
2.带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。
也就是说,AVL树,本质上是带了平衡功能的二叉查找树(二叉排序树,二叉搜索树)。
复杂度
算法 | 平均 | 最差 |
---|---|---|
空间 | O(n) | O(n) |
搜索 | O(log n) | O(log n) |
插入 | O(log n) | O(log n) |
删除 | O(log n) | O(log n) |
展示
非AVL树AVL树
查找操作:
可以像普通二叉查找树一样的进行,所以耗费O(log n)时间,因为AVL树总是保持平衡的。不需要特殊的准备,树的结构不会由于查找而改变。
删除操作
从AVL树中删除,可以通过把要删除的节点向下旋转成一个叶子节点,接着直接移除这个叶子节点来完成。因为在旋转成叶子节点期间最多有log n个节点被旋转,而每次AVL旋转耗费固定的时间,所以删除处理在整体上耗费O(log n) 时间。
插入操作
假设平衡因子是左子树的高度减去右子树的高度所得到的值,又假设由于在二叉排序树上插入节点而失去平衡的最小子树根节点的指针为a(即a是离插入点最近,且平衡因子绝对值超过1的祖先节点),则失去平衡后进行的规律可归纳为下列四种情况:
-
单向右旋平衡处理LL:即因为在根节点的左孩子的左子树添加了新节点,导致根节点的平衡因子变为 +2,二叉树失去平衡。对于这种情况,对节点 n 右旋一次即可。
LL - 单向左旋平衡处理RR:RR 型的情况和 LL 型完全对称。只需要对节点 n 进行一次左旋即可修正。
-
双向旋转(先左后右)平衡处理LR:LR 就是将新的节点插入到了 n 的左孩子的右子树上导致的不平衡的情况。这时我们需要的是先对 i 进行一次左旋再对 n 进行一次右旋。
LR - 双向旋转(先右后左)平衡处理RL:RL 就是将新的节点插入到了 n 的右孩子的左子树上导致的不平衡的情况。这时我们需要的是先对 i 进行一次右旋再对 n 进行一次左旋。
在平衡的二叉排序树BBST (Balancing Binary Search Tree)上插入一个新的数据元素e的递归算法可描述如下:
- 若BBST为空树,则插入一个数据元素为e的新节点作为BBST的根节点,树的深度增1;
- 若e的关键字和BBST的根节点的关键字相等,则不进行;
- 若e的关键字小于BBST的根节点的关键字,而且在BBST的左子树中不存在和e有相同关键字的节点,则将e插入在BBST的左子树上,并且当插入之后的左子树深度增加(+1)时,分别就下列不同情况处理之:
(1)BBST的根节点的平衡因子为-1(右子树的深度大于左子树的深度,则将根节点的平衡因子更改为0,BBST的深度不变;
(2)BBST的根节点的平衡因子为0(左、右子树的深度相等):则将根节点的平衡因子更改为1,BBST的深度增1;
(3)BBST的根节点的平衡因子为1(左子树的深度大于右子树的深度):则若BBST的左子树根节点的平衡因子为1:则需进行单向右旋平衡处理,并且在右旋处理之后,将根节点和其右子树根节点的平衡因子更改为0,树的深度不变; - 若e的关键字大于BBST的根节点的关键字,而且在BBST的右子树中不存在和e有相同关键字的节点,则将e插入在BBST的右子树上,并且当插入之后的右子树深度增加(+1)时,分别就不同情况处理之。
旋转操作
在数据结构中,树旋转是在二叉树中的一种子树调整操作, 每一次旋转并不影响对该二叉树进行中序遍历的结果. 树旋转通常应用于需要调整树的局部平衡性的场合。
以下图表以四列表示四种情况,每行表示在该种情况下要进行的操作。在左左和右右的情况下,只需要进行一次旋转操作;在左右和右左的情况下,需要进行两次旋转操作。
python
class TreeNode(object):
def __init__(self, data, left=None, right=None):
self.data = data
self.left = left
self.right = right
self.height = 0
class AVLTree(object):
def __init__(self):
self.root = None
def find(self, key):
if not self.root:
return None
else:
return self._find(key, self.root)
def _find(self, key, node):
if not node:
return None
elif key < node.data:
return self._find(key, node.left)
elif key > node.data:
return self._find(key, node.right)
else:
return node
def findMin(self):
if self.root is None:
return None
else:
return self._findMin(self.root)
def _findMin(self, node):
if node.left:
return self._findMin(node.left)
else:
return node
def findMax(self):
if self.root is None:
return None
else:
return self._findMax(self.root)
def _findMax(self, node):
if node.right:
return self._findMax(node.right)
else:
return node
def height(self, node):
if node is None:
return -1
else:
return node.height
#在node节点的左孩子k1的左子树添加了新节点,左旋转
def singleLeftRotate(self, node):
k1 = node.left
node.left = k1.right
k1.right = node
node.height = max(self.height(node.right), self.height(node.left)) + 1
k1.height = max(self.height(k1.left), node.height) + 1
return k1
#在node节点的右孩子k1的右子树添加了新节点,右旋转
def singleRightRotate(self, node):
k1 = node.right
node.right = k1.left
k1.left = node
node.height = max(self.height(node.right), self.height(node.left)) + 1
k1.height = max(self.height(k1.right), node.height) + 1
return k1
#在node节点的左孩子的右子树添加了新节点,先左后右
def doubleRightRotate(self, node):
node.right = self.singleLeftRotate(node.right)
return self.singleRightRotate(node)
#在node节点的右孩子的左子树添加了新节点,先右后左
def doubleLeftRotate(self, node):
node.left = self.singleRightRotate(node.left)
return self.singleLeftRotate(node)
def insert(self, key):
if not self.root:
self.root = TreeNode(key)
else:
self.root = self._insert(key, self.root)
def _insert(self, key, node):
if node is None:
node = TreeNode(key)
elif key < node.data:
node.left = self._insert(key, node.left)
if (self.height(node.left) - self.height(node.right)) == 2:
if key < node.left.data:
node = self.singleLeftRotate(node)
else:
node = self.doubleLeftRotate(node)
elif key > node.data:
node.right = self._insert(key, node.right)
if (self.height(node.right) - self.height(node.left)) == 2:
if key > node.right.data:
node = self.singleRightRotate(node)
else:
node = self.doubleRightRotate(node)
node.height = max(self.height(node.right), self.height(node.left)) + 1
return node
def delete(self, key):
if self.root is None:
raise KeyError('Error,empty tree')
else:
self.root = self._delete(key, self.root)
def _delete(self, key, node):
if node is None:
raise KeyError('Error,key not in tree')
elif key < node.data:
node.left = self._delete(key, node.left)
if (self.height(node.right) - self.height(node.left)) == 2:
if self.height(node.right.right) >= self.height(node.right.left):
node = self.singleRightRotate(node)
else:
node = self.doubleRightRotate(node)
node.height = max(self.height(node.left), self.height(node.right)) + 1
elif key > node.data:
node.right = self._delete(key, node.right)
if (self.height(node.left) - self.height(node.right)) == 2:
if self.height(node.left.left) >= self.height(node.left.right):
node = self.singleLeftRotate(node)
else:
node = self.doubleLeftRotate(node)
node.height = max(self.height(node.left), self.height(node.right)) + 1
elif node.left and node.right:
if node.left.height <= node.right.height:
minNode = self._findMin(node.right)
node.key = minNode.key
node.right = self._delete(node.key, node.right)
else:
maxNode = self._findMax(node.left)
node.key = maxNode.key
node.left = self._delete(node.key, node.left)
node.height = max(self.height(node.left), self.height(node.right)) + 1
else:
if node.right:
node = node.right
else:
node = node.left
return node