红黑树:一种特殊的二叉搜索树
二叉搜索树:一种树的类型,每个节点最多有两个子节点,其中其左节点一定小于当前节点,右节点一定大于当前节点
二叉树的缺点:如果给定的初始序列顺序不好,可能会建出类似于链表的结构,对搜索速度全无助益
红黑树的目的:构建一棵趋于平衡的二叉搜索树,杜绝bad case的出现情况
红黑树树节点的组成
红黑树的特点
合法红黑树的性质
其中,最后一个性质确保了红黑树的相对“平衡”
*为了便于处理边界条件,红黑树中没有节点的孩子会直接指向None,通过定义一个通用哨兵NIL来代指None
NIL's param settings
parent = None
color = BLACK
left = None
right = None
value = None
右图为红黑树的包含哨兵(nil)状态,为了方便,后图都将采用左图的方式绘制
旋转需要满足如下要求:
至于其他节点,由于存在哨兵NIL,变得易于处理,以左旋为例:
下面给出左旋和右旋的python代码
def _left_rotate(self, node):
if node.right == self.nil:
print("can't left rotate")
return
right = node.right
parent = node.parent
isLeft = parent.left == node
if self.root == node:
self.root = right
if isLeft:
parent.left = right
else:
parent.right = right
right.parent = parent
node.right = right.left
right.left.parent = node
right.left = node
node.parent = right
def _right_rotate(self, node):
if node.left == self.nil:
print("can't right rotate")
return
left = node.left
parent = node.parent
isLeft = parent.left == node
if self.root == node:
self.root = left
if isLeft:
parent.left = left
else:
parent.right = left
left.parent = parent
node.left = left.right
left.right.parent = node
left.right = node
node.parent = left
说是建树,其实就是树节点的插入操作
目标:让插入的节点同时满足二叉搜索树的要求和合法红黑树的性质
第一个目标很容易达成,就是找到该节点应该插入的位置然后把它塞进去
def insert(self, value):
# Node(value, color, parent, left, right)
insert_node = Node(value, NC.RED, None, self.nil, self.nil)
curr_node = self.root
while True:
if curr_node.right == self.nil and value >= curr_node.value:
curr_node.right = insert_node
break
if curr_node.left == self.nil and value <= curr_node.value:
curr_node.left = insert_node
break
curr_node = curr_node.right if value >= curr_node.value else curr_node.left
insert_node.parent = curr_node
# self._adjust(insert_node)
接下来看看第二个目标,回顾一下之前的5条性质
对应下来
所以我们需要通过调整树结构和树节点颜色,来确保性质4的有效性,接下来将针对不同的树结构完成对合法红黑树的调整
在分析不同case之前,先给出一些定义
这种情况比较简单,将该节点的颜色设置为黑色即可
不需要下一步的处理了,如果父节点是黑色的,而插入的子节点默认为红色,不会影响性质4的有效性,无需调整树结构
所以!接下来的所有case当中,都会默认parent.color=RED!
直观来说,case3一定满足下图中4种情况中的一种(插入N前的树一定是valid的红黑树)
这种情况下,我们改变P、G、U的颜色,从而在维持黑高(性质5)的情况下,让红色节点和红色节点不相接(性质4)
但改变了G的颜色,使之成为RED节点之后,可能在G和G.parent之间存在性质4的冲突,所以将G看作新插入的节点N,重新判定当前情况
这种case下一定是下面4种状态之一,且一定出现在循环当中(因为P、G、U构成的子树在N插入前就不满足valid红黑树了)
也就是N的左右孩子一定非NIL,且有黑色节点
不难发现,对于右边两种情况来说,只需要对G做一次旋转就可以让这棵子树平衡;而左边的两种情况又可以通过旋转P达到和左边一样的结构
因此,我们对case4再分为2种情况,一种为三角结构(左侧2图),一种为线性结构(右侧2图)
目标:变为线性结构,可以和右侧两图统一处理
解决方式:
以第三张图为例,给出了处理方案
在处理后满足下一个处理的Node颜色为BLACK,不需要再次判定其与其parent的性质4合法性,因此可以结束树结构的调整
以上4种case的python代码如下
def _adjust(self, node):
# case1: node is root
if node == self.root:
node.color = NC.BLACK
return
# if node.parent is BLACK, the RBT is already valid
# so grandparent is ALWAYS BLACK if continue processing
if node.parent.color == NC.BLACK:
return
# pick up uncle, parent and grandparent(if parent is red, there must be grandparent)
uncle, parent, grandparent, subtree_type = self._get_relative_nodes_and_subtree_type(node)
# case2: uncle is red (implies grandparent is BLACK, parent is RED)
if uncle.color == NC.RED:
# change the color of uncle, parent and grand
uncle.color = NC.BLACK
parent.color = NC.BLACK
grandparent.color = NC.RED
return self._adjust(grandparent)
# case3: uncle is black (this case includes "without uncle" cause nil node is black)
# case3.1: deal with triangle tree type first
if subtree_type == SBT.TRI_R:
self._right_rotate(parent)
subtree_type = SBT.LIN_R
node, parent = parent, node
if subtree_type == SBT.TRI_L:
self._left_rotate(parent)
subtree_type = SBT.LIN_L
node, parent = parent, node
# case3.2: deal with line tree type
if subtree_type == SBT.LIN_R:
self._left_rotate(grandparent)
if subtree_type == SBT.LIN_L:
self._right_rotate(grandparent)
parent.color = NC.BLACK
grandparent.color = NC.RED
@staticmethod
def _get_relative_nodes_and_subtree_type(node):
parent = node.parent
grandparent = parent.parent
if parent.left == node:
if grandparent.left == parent:
subtree_type = SBT.LIN_L
uncle = grandparent.right
else:
subtree_type = SBT.TRI_R
uncle = grandparent.left
else:
if grandparent.left == parent:
subtree_type = SBT.TRI_L
uncle = grandparent.right
else:
subtree_type = SBT.LIN_R
uncle = grandparent.left
return uncle, parent, grandparent, subtree_type