伟大数据结构 ‘树’ 理论

wang 发布于 2024-07-10 22 次阅读


名词:

节点的度: 一个节点含有的 子节点的个数

树的度:一棵树中,最大的节点的度为树的度

叶节点或终端节点: 度数为0的节点

孩子节点或子节点: 一个节点含有 子树的根节点

兄弟节点: 具有相同父节点的节点 互称为兄弟节点

节点的层次:从根开始定义起,根为第一层,根的子节点为第二层,.....

树的高度或深度:树中节点最大的层次

堂兄弟节点:父节点在同一层的节点

节点的祖先:从根到该节点所经分支上的所有节点

子孙:以某节点为根的字数中任意节点都成为该节点的子孙

森林:由m(m>=0)棵互不相交的树的集合称为森林

树的储存结构

可以使用list来存 (内存地址连续)

可以使用Listnode来存(其有left right 与 node)节点

树的遍历方式

先从 最小的数开始遍历

先序遍历: 根 左 右

中序遍历:左 根 右

后序遍历: 左右根

树的种类

满二叉树与完全二叉树

平衡二叉树与失衡二叉树

镜像二叉树与对称二叉树

宇宙无敌树 "红黑树"

历史演变

1962年: AVL 树 (Adelson-Velsky, Landis)
为极致查询速度而生,通过严格平衡避免普通二叉树退化,但维护成本高。

1972年: 对称二叉B树 (Rudolf Bayer)
将B树思想引入二叉树,以近似平衡换取更低的更新开销。

1978年: 红黑树 (Guibas, Sedgewick)
通过最终确立现代命名与颜色规则,红黑树塑造了一个在增、删、查操作上综合性能最优的平衡结构。它完美权衡了查询速度与更新成本,使其成为构建大型、动态数据系统的理想基础,最终在实践应用中取代了AVL树的主流地位。

什么是红黑树?

其作用:

普通的二叉查找树在极端情况下会退化成链表。例如,你依次插入 1, 2, 3, 4, 5,那么这棵树会变成一个长长的右子树,查找效率改变“O(n)”

红黑树通过设定约束条件,确保没有一条路径会比其他路径长出两倍以上,从而维持了“大致平衡”保证高效性

红黑树的五大规则(必须)

颜色属性
每个节点非红即黑。

根节点
根节点必须是黑色。

叶子节点
所有叶子节点(NIL/null)视为黑色。

红色节点限制
红色节点的两个子节点必须都是黑色。(即:不能有两个连续的红色节点)

路径规则
从任一节点到其每个后代叶子节点的所有简单路径上,均包含相同数量的黑色节点。(这个数量称为“黑高”)

这就是红黑树




代码示例(自认为没人会用!)

名词解释

左旋

把左旋想象成「跷跷板」游戏
想象一个简单的跷跷板:
   父节点 (x)
    /   \
   A    子节点 (y)
        /   \
       B     C

左旋就是:把右边的重孩子(y)抬上来,把自己(x)压下去!

左旋后的结果:

     子节点 (y)
      /   \
   父节点(x)  C
    /   \
   A     B

以下代码为python3.12版本的

主要实现

  1. 完整的红黑树操作:插入、删除、搜索
  2. 旋转操作:左旋和右旋
  3. 修复操作:插入后修复和删除后修复
  4. 遍历功能:中序遍历返回排序结果
  5. Pythonic接口:支持 len() 和 in 操作
from typing import Any, Optional

class Node:
    """红黑树节点类"""
    
    __slots__ = ('key', 'value', 'left', 'right', 'parent', 'color')
    
    def __init__(self, key: Any, value: Any, color: str = 'RED'):
        self.key = key          # 节点键
        self.value = value      # 节点值
        self.left = None       # 左子节点
        self.right = None      # 右子节点
        self.parent = None     # 父节点
        self.color = color     # 节点颜色 ('RED' 或 'BLACK')
    
    def __repr__(self) -> str:
        return f"Node({self.key}: {self.value}, {self.color})"

class RedBlackTree:
    """红黑树实现"""
    
    def __init__(self):
        # 创建NIL节点(叶子节点,黑色)
        self.NIL = Node(None, None, 'BLACK')
        self.root = self.NIL    # 根节点初始化为NIL
        self.size = 0          # 树中元素个数
    
    def _left_rotate(self, x: Node) -> None:
        """左旋转操作"""
        y = x.right            # 设置y为x的右子节点
        x.right = y.left       # 将y的左子树变为x的右子树
        
        if y.left != self.NIL:
            y.left.parent = x  # 如果y的左子节点存在,设置其父节点为x
        
        y.parent = x.parent    # 将x的父节点赋给y
        
        # 更新父节点的子节点引用
        if x.parent == self.NIL:
            self.root = y      # 如果x是根节点,则y成为新的根节点
        elif x == x.parent.left:
            x.parent.left = y  # 如果x是左子节点,则y成为新的左子节点
        else:
            x.parent.right = y # 如果x是右子节点,则y成为新的右子节点
        
        y.left = x             # 将x放在y的左边
        x.parent = y           # 将x的父节点设置为y
    
    def _right_rotate(self, y: Node) -> None:
        """右旋转操作"""
        x = y.left             # 设置x为y的左子节点
        y.left = x.right       # 将x的右子树变为y的左子树
        
        if x.right != self.NIL:
            x.right.parent = y # 如果x的右子节点存在,设置其父节点为y
        
        x.parent = y.parent    # 将y的父节点赋给x
        
        # 更新父节点的子节点引用
        if y.parent == self.NIL:
            self.root = x      # 如果y是根节点,则x成为新的根节点
        elif y == y.parent.right:
            y.parent.right = x # 如果y是右子节点,则x成为新的右子节点
        else:
            y.parent.left = x  # 如果y是左子节点,则x成为新的左子节点
        
        x.right = y            # 将y放在x的右边
        y.parent = x           # 将y的父节点设置为x
    
    def insert(self, key: Any, value: Any) -> None:
        """插入键值对"""
        new_node = Node(key, value)
        new_node.left = self.NIL
        new_node.right = self.NIL
        
        # 查找插入位置
        parent = self.NIL
        current = self.root
        
        while current != self.NIL:
            parent = current
            if new_node.key < current.key:
                current = current.left
            elif new_node.key > current.key:
                current = current.right
            else:
                # 键已存在,更新值
                current.value = value
                return
        
        # 设置新节点的父节点
        new_node.parent = parent
        
        # 更新父节点的子节点引用
        if parent == self.NIL:
            self.root = new_node  # 树为空,新节点成为根节点
        elif new_node.key < parent.key:
            parent.left = new_node
        else:
            parent.right = new_node
        
        self.size += 1
        self._insert_fixup(new_node)  # 修复红黑树性质
    
    def _insert_fixup(self, node: Node) -> None:
        """插入后修复红黑树性质"""
        while node.parent.color == 'RED':
            # 父节点是祖父节点的左子节点
            if node.parent == node.parent.parent.left:
                uncle = node.parent.parent.right  # 叔节点
                
                # 情况1:叔节点是红色
                if uncle.color == 'RED':
                    node.parent.color = 'BLACK'
                    uncle.color = 'BLACK'
                    node.parent.parent.color = 'RED'
                    node = node.parent.parent
                else:
                    # 情况2:节点是父节点的右子节点
                    if node == node.parent.right:
                        node = node.parent
                        self._left_rotate(node)
                    
                    # 情况3:节点是父节点的左子节点
                    node.parent.color = 'BLACK'
                    node.parent.parent.color = 'RED'
                    self._right_rotate(node.parent.parent)
            else:
                # 对称情况:父节点是祖父节点的右子节点
                uncle = node.parent.parent.left   # 叔节点
                
                # 情况1:叔节点是红色
                if uncle.color == 'RED':
                    node.parent.color = 'BLACK'
                    uncle.color = 'BLACK'
                    node.parent.parent.color = 'RED'
                    node = node.parent.parent
                else:
                    # 情况2:节点是父节点的左子节点
                    if node == node.parent.left:
                        node = node.parent
                        self._right_rotate(node)
                    
                    # 情况3:节点是父节点的右子节点
                    node.parent.color = 'BLACK'
                    node.parent.parent.color = 'RED'
                    self._left_rotate(node.parent.parent)
        
        # 确保根节点为黑色
        self.root.color = 'BLACK'
    
    def search(self, key: Any) -> Optional[Any]:
        """查找键对应的值"""
        node = self._search_node(key)
        return node.value if node != self.NIL else None
    
    def _search_node(self, key: Any) -> Node:
        """查找指定键的节点"""
        current = self.root
        while current != self.NIL and key != current.key:
            if key < current.key:
                current = current.left
            else:
                current = current.right
        return current
    
    def delete(self, key: Any) -> bool:
        """删除指定键的节点"""
        node = self._search_node(key)
        if node == self.NIL:
            return False  # 节点不存在
        
        self._delete_node(node)
        self.size -= 1
        return True
    
    def _delete_node(self, node: Node) -> None:
        """删除节点核心逻辑"""
        original_color = node.color
        x = self.NIL
        
        if node.left == self.NIL:
            x = node.right
            self._transplant(node, node.right)
        elif node.right == self.NIL:
            x = node.left
            self._transplant(node, node.left)
        else:
            # 有两个子节点,找后继节点
            successor = self._minimum(node.right)
            original_color = successor.color
            x = successor.right
            
            if successor.parent == node:
                x.parent = successor
            else:
                self._transplant(successor, successor.right)
                successor.right = node.right
                successor.right.parent = successor
            
            self._transplant(node, successor)
            successor.left = node.left
            successor.left.parent = successor
            successor.color = node.color
        
        # 如果删除的是黑色节点,需要修复
        if original_color == 'BLACK':
            self._delete_fixup(x)
    
    def _transplant(self, u: Node, v: Node) -> None:
        """用节点v替换节点u"""
        if u.parent == self.NIL:
            self.root = v
        elif u == u.parent.left:
            u.parent.left = v
        else:
            u.parent.right = v
        v.parent = u.parent
    
    def _minimum(self, node: Node) -> Node:
        """找到子树中的最小节点"""
        while node.left != self.NIL:
            node = node.left
        return node
    
    def _delete_fixup(self, node: Node) -> None:
        """删除后修复红黑树性质"""
        while node != self.root and node.color == 'BLACK':
            if node == node.parent.left:
                sibling = node.parent.right
                
                # 情况1:兄弟节点是红色
                if sibling.color == 'RED':
                    sibling.color = 'BLACK'
                    node.parent.color = 'RED'
                    self._left_rotate(node.parent)
                    sibling = node.parent.right
                
                # 情况2:兄弟节点的两个子节点都是黑色
                if sibling.left.color == 'BLACK' and sibling.right.color == 'BLACK':
                    sibling.color = 'RED'
                    node = node.parent
                else:
                    # 情况3:兄弟节点的右子节点是黑色
                    if sibling.right.color == 'BLACK':
                        sibling.left.color = 'BLACK'
                        sibling.color = 'RED'
                        self._right_rotate(sibling)
                        sibling = node.parent.right
                    
                    # 情况4:兄弟节点的右子节点是红色
                    sibling.color = node.parent.color
                    node.parent.color = 'BLACK'
                    sibling.right.color = 'BLACK'
                    self._left_rotate(node.parent)
                    node = self.root
            else:
                # 对称情况
                sibling = node.parent.left
                
                if sibling.color == 'RED':
                    sibling.color = 'BLACK'
                    node.parent.color = 'RED'
                    self._right_rotate(node.parent)
                    sibling = node.parent.left
                
                if sibling.right.color == 'BLACK' and sibling.left.color == 'BLACK':
                    sibling.color = 'RED'
                    node = node.parent
                else:
                    if sibling.left.color == 'BLACK':
                        sibling.right.color = 'BLACK'
                        sibling.color = 'RED'
                        self._left_rotate(sibling)
                        sibling = node.parent.left
                    
                    sibling.color = node.parent.color
                    node.parent.color = 'BLACK'
                    sibling.left.color = 'BLACK'
                    self._right_rotate(node.parent)
                    node = self.root
        
        node.color = 'BLACK'
    
    def inorder_traversal(self) -> list:
        """中序遍历返回排序后的键值对列表"""
        result = []
        self._inorder(self.root, result)
        return result
    
    def _inorder(self, node: Node, result: list) -> None:
        """中序遍历辅助函数"""
        if node != self.NIL:
            self._inorder(node.left, result)
            result.append((node.key, node.value, node.color))
            self._inorder(node.right, result)
    
    def __len__(self) -> int:
        """返回树中元素个数"""
        return self.size
    
    def __contains__(self, key: Any) -> bool:
        """检查键是否在树中"""
        return self.search(key) is not None

# 测试代码
if __name__ == "__main__":
    rbt = RedBlackTree()
    
    # 插入测试
    test_data = [(5, "five"), (3, "three"), (7, "seven"), 
                 (2, "two"), (4, "four"), (6, "six"), (8, "eight")]
    
    print("插入数据:", test_data)
    for key, value in test_data:
        rbt.insert(key, value)
    
    # 遍历测试
    print("\n中序遍历结果:")
    for item in rbt.inorder_traversal():
        print(f"Key: {item[0]}, Value: {item[1]}, Color: {item[2]}")
    
    # 搜索测试
    print(f"\n搜索测试:")
    print(f"search(4) = {rbt.search(4)}")
    print(f"search(10) = {rbt.search(10)}")
    print(f"5 in tree: {5 in rbt}")
    
    # 删除测试
    print(f"\n删除测试:")
    print(f"删除键4: {rbt.delete(4)}")
    print(f"删除后搜索4: {rbt.search(4)}")
    print(f"树的大小: {len(rbt)}")
    
    print("\n删除后的中序遍历:")
    for item in rbt.inorder_traversal():
        print(f"Key: {item[0]}, Value: {item[1]}, Color: {item[2]}")
一名热爱海贼的AI开发者
最后更新于 2025-11-15