在树的结构中,一个最重要的用途是用作二叉搜索树。接下来使用搜索树结构有效的实现有序映射。
二叉搜索树的结构特性产生的最重要的结果是搜索算法。在search中,搜索一次下降一层,树高为h,每一个节点的搜索时间为O(1)(至于为什么是O(1),则涉及到哈希表的设计),则最坏情况下总的搜索时间为O(h)。
基于二叉搜索树,提供了聊两种性能更高的搜索树算法。分别是AVL树,伸展树。当然还有红黑树和多路搜索树,改日再聊。
1. AVL树
AVL树中定义的一个属性为,对于T中的每一个位置p,p的孩子的高度最多相差1。一棵有n个节点的AVL树的高度为O(logn)。(LeetCode经常遇到此类题)
"""
AVL树
"""
class AVLTreeMap():
class _Node():
__sloats__ = '_height, _left, _right, _parent, _element'
def __init__(self, element, parent=None, left=None, right=None):
self._height = 0
def left_height(self):
return self._left._height if self._left is not None else 0
def right_height(self):
return self._right._height if self._right is not None else 0
def _relink(self, parent, child, make_left_child):
"""正确关联父亲和孩子节点"""
if make_left_child:
parent._left = child # 使child成为左节点,child允许为None
else:
parent._right = child # 使child成为右节点,child允许为None
if child is not None:
child._parent = parent # 如果child存在,指向父节点(定义双指针)
def _rotate(self, p):
"""旋转节点和原来的祖父母节点进行关联"""
a = p._node
b = a._parent
c = b._parent
if c is None: # 这种情况是b为根节点,旋转之后a变为根节点
self._root = a
x._parent = None
else:
self._relink(c, a, b == c._left) # 如果b为c的左节点,就直接使a变为c的左节点
if a == b._left: # 如果a也为b的左节点,就使a的右节点变为b的左节点
self._relink(b, a._right, True)
self._relink(a, b, False)
else: # 如果a也为b的右节点,就使a的右节点变为b的右节点
self._relink(b, a._left, False)
self._relink(a, b, True)
def _restructure(self, x):
"""判断需要旋转一次还是两次"""
y = self.parent(x)
z = self.parent(y)
if (x == self.right(y)) == (y == self.right(z)): # 此时三个节点在一条直线上,旋转一次, 使y成为root节点
self._rotate(y)
return y
else:
self._rotate(x) # 旋转一次, 使三个节点成为一条线
self._rotate(x) # 旋转两次,使三个节点变平衡
return x
def _recompute_height(self, p):
"""计算树的高度"""
p._node._height = 1 + max(p._node.left_height(), p._node.right_height())
def _isbalanced(self, p):
"""判断树是否平衡"""
return abs(p._node.left_height() - p._node.right_height()) <= 1
def _tall_child(self, p, favorleft=False):
if p._node.left_height() + (1 if favorleft else 0) > p._node.right_height():
return self.left(p)
else:
return self.right(p)
def _tall_grandchild(self, p):
child = self._tall_child(p)
alignment = (child == self.left(p))
return self._tall_child(child, alignment)
def _rebalance(self, p):
"""恢复树的平衡"""
while p is not None:
old_height = p._node._height
if not self._rebalance(p):
p = self._restructure(self._tall_grandchild(p))
self._recompute_height(self.left(p))
self._recompute_height(self.right(p))
self._recompute_height(p)
if p._node._height == old_height:
p = None
else:
p = self.parent(p)
def _reblance_insert(self, p):
"""插入操作后重新使树平衡"""
self._rebalance(p)
def _reblance_delete(self, p):
"""删除操作后重新使树平衡"""
self._rebalance(p)
2. 伸展树
伸展树对树的高度没有严格的对数上界,它的效率体现在某一位置移动到根的操作,每次搜索、插入、删除都要从最底层位置开始、伸展操作会是的频繁访问的元素更快接近于根,从而减少典型的搜索时间。
"""
伸展树
"""
class SplayTreeMap():
def _rotate(self, p):
pass
def _splay(self, p):
while p != self.root():
parent = self.parent(p)
grand = self.parent(parent)
if grand is None:
# zig case
self._rotate(p)
elif (parent == self.left(grand)) == (p == self.left(parent)):
# zig-zag case
self._rotate(parent) # move parent up
self._rotate(p) # move p up
else:
# zig-zig case
self._rotate(p) # move p up
self._rotate(p) # move p up again
def _reblance_insert(self, p):
self._splay(p)
def _reblance_delete(self, p):
if p is not None:
self._splay(p)
def _reblance_access(self, p):
self._splay(p)
三种类型:
zig-zig 型: 三个节点在同一直线上
zig-zag型: 三个节点不在同一直线上
zig型:没有祖父节点
在最坏情况下,搜索的位置可能位于树的最深处,所以对一颗高度为为h的伸展树进行搜索、插入、删除操作中,全部运行时间为O(h), h最大可能接近n,所以最坏情况时间复杂度也为O(n)。