红黑树 - Red-Black Tree 原理与 C# 实现

什么是红黑树

红黑树是一种自平衡二叉搜索树,通过给节点着色(红色或黑色)并遵循特定规则,保证树在插入、删除操作后仍然保持平衡,从而确保操作的时间复杂度始终为 O(log n)。

核心特点

  • 每个节点要么是红色,要么是黑色
  • 根节点是黑色
  • 所有叶子节点(NIL)是黑色
  • 红色节点的两个子节点必须是黑色(不能有连续的红色节点)
  • 从任一节点到其叶子节点的所有路径包含相同数量的黑色节点

算法原理

红黑树的五条规则

复制代码
        13(B)                    红黑树示例
       /     \                   B = 黑色节点
     8(R)    17(B)              R = 红色节点
    /   \       \
  1(B)  11(B)  25(R)
              /   \
           22(B) 27(B)

为什么需要这些规则?

  • 规则 1-3:基础定义
  • 规则 4:防止路径过长(红色节点不能连续)
  • 规则 5:保证平衡性(所有路径黑色节点数相同)

旋转操作

红黑树通过旋转重新着色维持平衡:

左旋

复制代码
    x              y
   / \            / \
  a   y    →     x   c
     / \        / \
    b   c      a   b

右旋

复制代码
    y              x
   / \            / \
  x   c    →     a   y
 / \                / \
a   b              b   c

应用场景

1. C++ STL 容器

  • std::map:有序键值对存储
  • std::set:有序集合
  • std::multimapstd::multiset

2. Java 集合框架

  • TreeMap:有序映射
  • TreeSet:有序集合
  • ConcurrentSkipListMap(部分实现)

3. Linux 内核

  • 进程调度器(CFS):完全公平调度器使用红黑树管理进程
  • 虚拟内存管理:管理内存区域

4. 数据库索引

  • MySQL InnoDB:部分索引结构
  • 内存数据库:有序索引

5. 实际应用

  • 🔍 搜索引擎:倒排索引
  • 📊 统计系统:有序排名
  • 定时器:按时间排序的任务队列
  • 🎮 游戏开发:排行榜系统

C# 实现

节点定义

csharp 复制代码
public enum NodeColor
{
    Red,
    Black
}

public class RedBlackNode<T> where T : IComparable<T>
{
    public T Value { get; set; }
    public NodeColor Color { get; set; }
    public RedBlackNode<T> Left { get; set; }
    public RedBlackNode<T> Right { get; set; }
    public RedBlackNode<T> Parent { get; set; }
    
    public RedBlackNode(T value)
    {
        Value = value;
        Color = NodeColor.Red; // 新节点默认红色
    }
    
    public bool IsRed() => Color == NodeColor.Red;
    public bool IsBlack() => Color == NodeColor.Black;
}

红黑树主类

csharp 复制代码
public class RedBlackTree<T> where T : IComparable<T>
{
    private RedBlackNode<T> root;
    private readonly RedBlackNode<T> NIL; // 哨兵节点
    
    public RedBlackTree()
    {
        NIL = new RedBlackNode<T>(default(T)) { Color = NodeColor.Black };
        root = NIL;
    }
    
    /// <summary>
    /// 插入节点
    /// </summary>
    public void Insert(T value)
    {
        var newNode = new RedBlackNode<T>(value)
        {
            Left = NIL,
            Right = NIL,
            Parent = NIL
        };
        
        RedBlackNode<T> parent = NIL;
        RedBlackNode<T> current = root;
        
        // 找到插入位置
        while (current != NIL)
        {
            parent = current;
            if (newNode.Value.CompareTo(current.Value) < 0)
                current = current.Left;
            else
                current = current.Right;
        }
        
        newNode.Parent = parent;
        
        if (parent == NIL)
            root = newNode; // 空树
        else if (newNode.Value.CompareTo(parent.Value) < 0)
            parent.Left = newNode;
        else
            parent.Right = newNode;
        
        // 修复红黑树性质
        FixInsert(newNode);
    }
    
    /// <summary>
    /// 插入后修复
    /// </summary>
    private void FixInsert(RedBlackNode<T> node)
    {
        while (node.Parent.IsRed())
        {
            if (node.Parent == node.Parent.Parent.Left)
            {
                var uncle = node.Parent.Parent.Right;
                
                if (uncle.IsRed())
                {
                    // Case 1: 叔叔是红色 - 重新着色
                    node.Parent.Color = NodeColor.Black;
                    uncle.Color = NodeColor.Black;
                    node.Parent.Parent.Color = NodeColor.Red;
                    node = node.Parent.Parent;
                }
                else
                {
                    if (node == node.Parent.Right)
                    {
                        // Case 2: 叔叔是黑色,节点是右子 - 左旋
                        node = node.Parent;
                        LeftRotate(node);
                    }
                    // Case 3: 叔叔是黑色,节点是左子 - 右旋和重新着色
                    node.Parent.Color = NodeColor.Black;
                    node.Parent.Parent.Color = NodeColor.Red;
                    RightRotate(node.Parent.Parent);
                }
            }
            else
            {
                // 对称情况(父节点是祖父的右子)
                var uncle = node.Parent.Parent.Left;
                
                if (uncle.IsRed())
                {
                    node.Parent.Color = NodeColor.Black;
                    uncle.Color = NodeColor.Black;
                    node.Parent.Parent.Color = NodeColor.Red;
                    node = node.Parent.Parent;
                }
                else
                {
                    if (node == node.Parent.Left)
                    {
                        node = node.Parent;
                        RightRotate(node);
                    }
                    node.Parent.Color = NodeColor.Black;
                    node.Parent.Parent.Color = NodeColor.Red;
                    LeftRotate(node.Parent.Parent);
                }
            }
        }
        root.Color = NodeColor.Black; // 根节点始终为黑色
    }
    
    /// <summary>
    /// 左旋
    /// </summary>
    private void LeftRotate(RedBlackNode<T> x)
    {
        var y = x.Right;
        x.Right = y.Left;
        
        if (y.Left != NIL)
            y.Left.Parent = x;
        
        y.Parent = x.Parent;
        
        if (x.Parent == NIL)
            root = y;
        else if (x == x.Parent.Left)
            x.Parent.Left = y;
        else
            x.Parent.Right = y;
        
        y.Left = x;
        x.Parent = y;
    }
    
    /// <summary>
    /// 右旋
    /// </summary>
    private void RightRotate(RedBlackNode<T> y)
    {
        var x = y.Left;
        y.Left = x.Right;
        
        if (x.Right != NIL)
            x.Right.Parent = y;
        
        x.Parent = y.Parent;
        
        if (y.Parent == NIL)
            root = x;
        else if (y == y.Parent.Right)
            y.Parent.Right = x;
        else
            y.Parent.Left = x;
        
        x.Right = y;
        y.Parent = x;
    }
    
    /// <summary>
    /// 搜索节点
    /// </summary>
    public bool Search(T value)
    {
        return SearchNode(root, value) != NIL;
    }
    
    private RedBlackNode<T> SearchNode(RedBlackNode<T> node, T value)
    {
        if (node == NIL || value.CompareTo(node.Value) == 0)
            return node;
        
        if (value.CompareTo(node.Value) < 0)
            return SearchNode(node.Left, value);
        else
            return SearchNode(node.Right, value);
    }
    
    /// <summary>
    /// 中序遍历(有序输出)
    /// </summary>
    public List<T> InOrderTraversal()
    {
        var result = new List<T>();
        InOrderHelper(root, result);
        return result;
    }
    
    private void InOrderHelper(RedBlackNode<T> node, List<T> result)
    {
        if (node == NIL) return;
        
        InOrderHelper(node.Left, result);
        result.Add(node.Value);
        InOrderHelper(node.Right, result);
    }
}

使用示例

csharp 复制代码
class Program
{
    static void Main()
    {
        var tree = new RedBlackTree<int>();
        
        // 插入数据
        int[] values = { 13, 8, 17, 1, 11, 15, 25, 6, 22, 27 };
        
        Console.WriteLine("插入元素:");
        foreach (var value in values)
        {
            Console.Write($"{value} ");
            tree.Insert(value);
        }
        Console.WriteLine("\n");
        
        // 有序遍历
        Console.WriteLine("中序遍历(有序输出):");
        var sorted = tree.InOrderTraversal();
        Console.WriteLine(string.Join(" ", sorted));
        // 输出: 1 6 8 11 13 15 17 22 25 27
        
        // 搜索元素
        Console.WriteLine("\n搜索测试:");
        Console.WriteLine($"查找 11: {tree.Search(11)}"); // True
        Console.WriteLine($"查找 20: {tree.Search(20)}"); // False
        
        // 实际应用示例:排行榜系统
        var leaderboard = new RedBlackTree<int>();
        leaderboard.Insert(9500);
        leaderboard.Insert(8200);
        leaderboard.Insert(9800);
        leaderboard.Insert(7600);
        
        Console.WriteLine("\n排行榜(分数从低到高):");
        var rankings = leaderboard.InOrderTraversal();
        for (int i = 0; i < rankings.Count; i++)
        {
            Console.WriteLine($"第 {i + 1} 名: {rankings[i]} 分");
        }
    }
}

性能对比

数据结构 插入 删除 查找 优势
红黑树 O(log n) O(log n) O(log n) 综合性能好,保证最坏情况
AVL树 O(log n) O(log n) O(log n) 查找更快,但旋转次数多
普通BST O(n) O(n) O(n) 可能退化成链表
跳表 O(log n) O(log n) O(log n) 实现简单,支持并发

红黑树 vs AVL树

  • 红黑树:插入/删除更快(平均旋转少),适合频繁修改
  • AVL树:查找更快(更严格平衡),适合读多写少

常见问题

Q: 为什么新插入的节点是红色?

A: 红色节点不影响黑色高度,修复起来更简单。如果是黑色会立即违反规则5。

Q: 红黑树和 AVL 树如何选择?

A:

  • 插入删除频繁:红黑树(如 Linux 内核)
  • 查询为主:AVL 树(数据库索引)

Q: 实际开发中何时自己实现红黑树?

A: 几乎不需要!使用现成的:

  • C#: SortedDictionary<K,V>SortedSet<T>
  • Java: TreeMapTreeSet
  • C++: std::mapstd::set

Q: 红黑树的最大高度是多少?

A: 最大高度 ≤ 2log(n+1),保证了 O(log n) 的性能。

.NET 中的红黑树应用

csharp 复制代码
// C# 内置的红黑树实现
using System.Collections.Generic;

// SortedDictionary 底层使用红黑树
var sortedDict = new SortedDictionary<int, string>
{
    { 3, "Three" },
    { 1, "One" },
    { 2, "Two" }
};

// 自动按键排序
foreach (var item in sortedDict)
{
    Console.WriteLine($"{item.Key}: {item.Value}");
}
// 输出: 1: One, 2: Two, 3: Three

// SortedSet 也使用红黑树
var sortedSet = new SortedSet<int> { 5, 2, 8, 1 };
Console.WriteLine(string.Join(", ", sortedSet));
// 输出: 1, 2, 5, 8

总结

红黑树是工业级平衡树

  • 性能稳定:最坏情况仍为 O(log n)
  • 插入删除高效:平均旋转次数少
  • 广泛应用:STL、Java、Linux 内核
  • 实现复杂:但有现成库可用

何时使用红黑树?

  • 需要有序数据结构
  • 插入删除操作频繁
  • 需要保证最坏情况性能

掌握红黑树原理,能帮你理解标准库实现,做出更好的数据结构选择!

相关推荐
前进之路91 小时前
Leetcode每日一练--47
数据结构·算法·leetcode
晨非辰2 小时前
【数据结构初阶系列】归并排序全透视:从算法原理全分析到源码实战应用
运维·c语言·数据结构·c++·人工智能·python·深度学习
泡沫冰@6 小时前
数据结构(20)
数据结构
Miraitowa_cheems9 小时前
LeetCode算法日记 - Day 106: 两个字符串的最小ASCII删除和
java·数据结构·算法·leetcode·深度优先
Alex艾力的IT数字空间9 小时前
设计既保持高性能又兼顾可移植性的跨平台数据结构
数据结构·分布式·算法·微服务·中间件·架构·动态规划
耳总是一颗苹果9 小时前
数据结构---顺序表
数据结构
未若君雅裁9 小时前
LeetCode 51 - N皇后问题 详解笔记
java·数据结构·笔记·算法·leetcode·剪枝
小王C语言10 小时前
优先算法---双指针和滑动窗口
数据结构
大袁同学13 小时前
【哈希hash】:程序的“魔法索引”,实现数据瞬移
数据结构·c++·算法·哈希算法·散列表
@卞15 小时前
ST 表相关练习题
数据结构·c++·算法