一、基本概念
平衡二叉树:
平衡二叉树是一种二叉搜索树,它的每个结点的左子树和右子树的高度之差不超过1。有AVL树、红黑树等类型的平衡二叉树。平衡二叉树的插入和删除操作会引起局部的旋转操作,以保证树的平衡。
红黑树:
红黑树也是一种自平衡二叉搜索树,除了满足二叉搜索树的性质之外,红黑树还满足以下5个性质,这些性质是用来保证红黑树在动态添加/删除节点时能自我平衡:
1. 每个节点非红即黑。
2. 根节点必须是黑色的。
3. 每个叶子节点(NIL节点,即空节点)都是黑色的。
4. 如果一个节点是红色的,则它的两个子节点都是黑色的。
5. 从任一节点到其每个叶子节点的所有路径都包含相同数量的黑色节点。
二、插入删除操作
平衡二叉树的插入和删除操作会引起局部的旋转操作,以保证树的平衡。具体来说:
1. 插入操作:如果插入一个节点后打破了树的平衡,那么进行比较少的旋转操作就可以调整回平衡。
2. 删除操作:如果删除一个节点后打破了树的平衡,那么进行比较少的旋转操作就可以调整回平衡。
平衡二叉树对于插入和删除的操作都需要保证在平衡二叉树中遵守平衡约束,因此相对比较复杂。
而红黑树在插入和删除节点时的平衡策略相对简单一些,具体如下:
1. 插入操作:按照二叉搜索树的规则进行插入,并将插入的新节点标记为红色。然后对红黑树进行必要的调整,以保持红黑树的5个性质。调整过程中使用了旋转和颜色变化的策略,来保证红黑树的5个特性。
2. 删除操作:同样按照二叉搜索树的规则进行删除,如果被删除的节点是红色的,就直接删除;如果是黑色的,则reb-black性质就被打破了,这时需要通过旋转、移动、染色等方式重新调整树的结构,以保证红黑树的5个特性。
三、代码实现
以下是红黑树和平衡二叉树的Python代码实现,其中涉及了节点的定义、旋转和颜色调整等操作:
# 平衡二叉树节点定义 class AVLNode: def __init__(self, val): self.val = val self.height = 0 self.left = None self.right = None # 红黑树节点定义 class RBNode: def __init__(self, val, color=0): self.val = val self.color = color # 0表示黑色,1表示红色 self.left = None self.right = None self.parent = None # AVL树中的旋转操作 def rotate_right(node: AVLNode) -> AVLNode: pivot = node.left node.left = pivot.right pivot.right = node node.height = max(get_height(node.left), get_height(node.right)) + 1 pivot.height = max(get_height(pivot.left), get_height(pivot.right)) + 1 return pivot def rotate_left(node: AVLNode) -> AVLNode: pivot = node.right node.right = pivot.left pivot.left = node node.height = max(get_height(node.left), get_height(node.right)) + 1 pivot.height = max(get_height(pivot.left), get_height(pivot.right)) + 1 return pivot # 红黑树中的旋转和颜色调整操作 def left_rotate(rb_node: RBNode): r_node = rb_node.right rb_node.right = r_node.left if r_node.left: r_node.left.parent = rb_node r_node.parent = rb_node.parent if rb_node.parent is None: root_node = r_node elif rb_node == rb_node.parent.left: rb_node.parent.left = r_node else: rb_node.parent.right = r_node r_node.left = rb_node rb_node.parent = r_node return root_node def right_rotate(rb_node: RBNode): l_node = rb_node.left rb_node.left = l_node.right if l_node.right: l_node.right.parent = rb_node l_node.parent = rb_node.parent if rb_node.parent == None: root_node = l_node elif rb_node == rb_node.parent.right: rb_node.parent.right = l_node else: rb_node.parent.left = l_node l_node.right = rb_node rb_node.parent = l_node return root_node def insert_fixup(rb_node: RBNode, root_node: RBNode): # 插入节点时,如果父节点为红色,则需要进行平衡调整 while rb_node.parent and rb_node.parent.color == 1: if rb_node.parent == rb_node.parent.parent.left: # 父节点为祖父节点的左孩子 uncle = rb_node.parent.parent.right if uncle and uncle.color == 1: # Case 1:叔叔节点为红色,则父节点和叔叔节点都改为黑色,祖父节点改为红色 rb_node.parent.color = 0 uncle.color = 0 rb_node.parent.parent.color = 1 rb_node = rb_node.parent.parent else: if rb_node == rb_node.parent.right: # Case 2:叔叔节点为黑色,且当前节点是父节点的右孩子,则需要左旋父节点 rb_node = rb_node.parent root_node = left_rotate(rb_node) # Case 3:叔叔节点为黑色,且当前节点是父节点的左孩子,则父节点变成黑色,祖父节点变成红色,然后右旋祖父节点 rb_node.parent.color = 0 rb_node.parent.parent.color = 1 root_node = right_rotate(rb_node.parent.parent) else: # 父节点为祖父节点的右孩子 uncle = rb_node.parent.parent.left if uncle and uncle.color == 1: # Case 1 rb_node.parent.color = 0 uncle.color = 0 rb_node.parent.parent.color = 1 rb_node = rb_node.parent.parent else: if rb_node == rb_node.parent.left: # Case 2 rb_node = rb_node.parent root_node = right_rotate(rb_node) # Case 3 rb_node.parent.color = 0 rb_node.parent.parent.color = 1 root_node = left_rotate(rb_node.parent.parent) root_node.color = 0 return root_node def insert_rbt(root_node: RBNode, val): rb_node = RBNode(val, 1) node = root_node while node: if val < node.val: if node.left: node = node.left else: node.left = rb_node rb_node.parent = node break else: if node.right: node = node.right else: node.right = rb_node rb_node.parent = node break root_node = insert_fixup(rb_node, root_node) return root_node
四、总结
红黑树和平衡二叉树都是一种在动态添加/删除节点时能自我平衡的二叉搜索树,但是它们之间还是有不少差异的。其中,平衡二叉树对于插入和删除的操作都需要保证在平衡二叉树中遵守平衡约束,因此相对比较复杂;而红黑树在插入和删除节点时的平衡策略相对简单一些,易于实现以及调试。因此,对于动态操作较为频繁、对性能要求较高的场景,红黑树可能是更好的选择。