⒈ 红黑树的属性
- 每个节点的颜色非红即黑
- 根节点永远是黑色
- 所有叶子节点的值都是 NULL,颜色都是黑色
- 红色节点的父节点和子节点都是黑色
- 从一个节点到其所属的叶子节点的任意路径上所包含的黑色节点的数量相等
- 一个节点到其最远叶子节点的路径上所包含的节点数量不会超过到其最近叶子节点的路径上所包含节点数量的两倍
⒉ 红黑树的高度及平衡
红黑树也是一种自平衡二叉树,但红黑树的平衡并不是像 AVL 树那样严格的平衡。红黑树的平衡指的是从根节点到任何叶子节点的路径上所包含的黑色节点的数量相等。
综上所述,设红黑树的节点总数为 n,红黑树的高度为h,从根节点到叶子节点的最短路径包含的节点数量为 k,则有 ,即 。由于红黑树从根节点到所有叶子节点路径上的黑色节点数量相等,所以红黑树黑色节点的总数最多为 ,即黑色节点的高度最多为 k 。而红黑树中红色节点的父节点和子节点都是黑色,所以 ,所以红黑树的高度 。
⒊ 红黑树节点的插入
- 首先按照 BST 的插入规则插入新的节点 x,所有新插入的节点默认都为红色
- 如果 x 为根节点,则将 x 的颜色标记为黑色
- 如果 x 不是根节点并且 x 的父节点不是黑色,则需要根据不同情形进行适当的重新着色或旋转节点来重新实现红黑树的平衡
设 x 的父节点为 p,p 的父节点为 g,p 的兄弟节点为 u
① 如果 u 为红色
② 如果 u 为黑色
ⅰ 如果 x 为 p 的左节点,p 为 g 的左节点
ⅱ 如果 x 为 p 的右节点,p 为 g 的左节点
ⅲ 如果 x 为 p 的右节点,p 为 g 的右节点(ⅰ 的镜像)
ⅳ 如果 x 为 p 的左节点,p 为 g 的右节点(ⅱ 的镜像)
代码实现
class Node:
def __init__(self, key):
self.item = key
self.parent = None
self.left = None
self.right = None
self.color = 1
def __repr__(self):
return str(self.__dict__)
def __str__(self):
return str(self.__dict__)
class RBTree:
def __init__(self):
self.root = None
def insert(self, k):
"""
插入节点
:param k:
:return:
"""
node = Node(k)
if self.root is None:
node.color = 0
self.root = node
return
current = self.root
while current is not None:
parent = current
if node.item < current.item:
current = current.left
else:
current = current.right
node.parent = parent
if node.item < parent.item:
parent.left = node
else:
parent.right = node
self.fix_tree(node)
def fix_tree(self, node):
"""
调整节点平衡
:param node:
:return:
"""
while node != self.root and node.parent.color == 1:
if node.parent == node.parent.parent.left:
u = node.parent.parent.right
if u.color == 1:
u.color = 0
node.parent.color = 0
node.parent.parent.color = 1
node = node.parent.parent
else:
if node == node.parent.right:
node = node.parent
self.left_rotate(node)
node.parent.color = 0
node.parent.parent.color = 1
self.right_rotate(node.parent.parent)
else:
u = node.parent.parent.left
if u.color == 1:
u.color = 0
node.parent.color = 0
node.parent.parent.color = 1
node = node.parent.parent
else:
if node == node.parent.left:
node = node.parent
self.right_rotate(node)
node.parent.color = 0
node.parent.parent.color = 1
self.left_rotate(node.parent.parent)
self.root.color = 0
def left_rotate(self, node):
new = node.right
node.right = new.left
if new.left is not None:
new.left.parent = node
new.parent = node.parent
if node.parent is None:
self.root = new
elif node == node.parent.left:
node.parent.left = new
else:
node.parent.right = new
new.left = node
node.parent = new
def right_rotate(self, node):
new = node.left
node.left = new.right
if new.right is not None:
new.right.parent = node
new.parent = node.parent
if node.parent is None:
self.root = new
elif node == node.parent.left:
node.parent.left = new
else:
node.parent.right = new
node.parent = new
new.right = node
rbt = RBTree()
rbt.insert(55)
rbt.insert(40)
rbt.insert(65)
rbt.insert(60)
rbt.insert(75)
rbt.insert(57)
print(rbt.root)
⒋ 红黑树节点的删除
节点删除按照 BST 的节点删除规则操作,最终要么删除一个叶子节点,要么删除一个只包含一个子节点的节点。设要删除的节点为 v,取代节点 v 的位置的节点为 u(红黑树中叶子节点为 NULL,所以红黑树中的节点删除不会删除真正的叶子节点,但会删除叶子节点的父节点,此时 u 为 NULL,红黑树中 NULL 为黑色)。
① u 和 v 中有一个红色节点,这种情况处理起来比较简单,只需要将 u 标记为黑色即可
② u 和 v 都是黑色,此时删除 v 会破坏红黑树的平衡,我们要做的就是恢复红黑树的平衡
设 v 的兄弟节点为 s,v 的父节点为 p
ⅰ 如果 s 为黑色,s 的子节点至少由一个为红色
设 s 的红色子节点为 r,根据 r 和 s 的相对位置,可以分为四种情况
a. s 为 p 的左子节点,r 为 s 的左子节点
b. s 为 p 的左子节点,r 为 s 的右子节点
c. s 为 p 的右子节点,r 为 s 的右子节点(a 的镜像)
d. s 为 p 的右子节点,r 为 s 的左子节点(b 的镜像)
ⅱ 如果 s 和 s 的子节点都为黑色
ⅲ 如果 s 为红色
代码实现
def search(self, node, k):
if node is None:
return None
if k == node.item:
return node
if k < node.item:
return self.search(node.left, k)
else:
return self.search(node.right, k)
def delete(self, node):
"""
节点删除
:param node:
:return:
"""
if isinstance(node, int):
node = self.search(self.root, node)
if node is None:
return
replace = self.find_replace(node)
double_black = ((replace is None or replace.color == 0) and (node.color == 0))
parent = node.parent
if parent is None:
sibling = None
elif node == parent.left:
sibling = parent.right
else:
sibling = parent.left
if replace is None:
if node == self.root:
self.root = None
else:
if double_black:
# node 和 replace 都是黑色
self.fix_double_black(node)
else:
# replace 是叶子节点,所以是黑色,故 node 是红色
if sibling is not None:
sibling.color = 1
if node == parent.left:
parent.left = None
else:
parent.right = None
return
if node.left is None or node.right is None:
if node == self.root:
node.item = replace.item
node.left = None
node.right = None
else:
if node == parent.left:
parent.left = replace
else:
parent.right = replace
replace.parent = parent
if double_black:
self.fix_double_black(replace)
else:
replace.color = 0
return
# 有两个子节点
temp = node.item
node.item = replace.item
replace.item = temp
self.delete(replace)
@staticmethod
def find_replace(node):
# 左右子节点都为空
if node.left is None and node.right is None:
return None
# 左右子节点都不为空
if node.left is not None and node.right is not None:
successor = node.right
while successor.left is not None:
successor = successor.left
return successor
# 左右子节点有一个不为空
if node.left is not None:
return node.left
else:
return node.right
def fix_double_black(self, node):
if node == self.root:
return
parent = node.parent
if parent is None:
sibling = None
elif node == parent.left:
sibling = parent.right
else:
sibling = parent.left
if sibling is None:
self.fix_double_black(parent)
else:
if sibling.color == 1:
sibling.color = 0
parent.color = 1
if sibling == parent.left:
self.right_rotate(parent)
else:
self.left_rotate(parent)
self.fix_double_black(node)
else:
if sibling.left is not None and sibling.left.color == 1:
if sibling == parent.left:
sibling.left.color = sibling.color
sibling.color = parent.color
self.right_rotate(parent)
else:
sibling.left.color = parent.color
self.right_rotate(sibling)
self.left_rotate(parent)
parent.color = 0
elif sibling.right is not None and sibling.right.color == 1:
if sibling == parent.left:
sibling.right.color = parent.color
self.left_rotate(sibling)
self.right_rotate(parent)
else:
sibling.right.color = sibling.color
sibling.color = parent.color
self.left_rotate(parent)
parent.color = 0
else:
sibling.color = 1
if parent.color == 0:
self.fix_double_black(parent)
else:
parent.color = 0
⒌ 红黑树与 AVL 的比较
- AVL 树的平衡相较于红黑树更加严格,所以节点数量相同的 AVL 树的高度通常会小于红黑树的高度,这样 AVL 树的搜索会更快
- 同样由于红黑树的平衡并没有 AVL 树的严格,所以在插入或删除节点时红黑树的旋转操作通常会比较少,这样红黑树的插入和删除操作通常会更快