深入理解 C++ 红黑树:从理论到实践

引言

在计算机科学领域,数据结构是构建高效算法的基石。而在众多的数据结构中,平衡二叉搜索树因其优秀的查找、插入和删除性能而备受关注。红黑树(Red-Black Tree)作为一种自平衡的二叉搜索树,更是在 C++ 标准库(如 STL 中的 map 和 set)中得到了广泛应用。本文将深入探讨红黑树的原理、实现及应用,帮助读者全面掌握这一重要的数据结构。

红黑树的基本概念

红黑树是一种特殊的二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色(红色或黑色)。通过对任何一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。

红黑树必须满足以下五个性质:

  1. 每个节点要么是红色,要么是黑色。
  2. 根节点是黑色。
  3. 每个叶子节点(NIL 节点,空节点)是黑色的。
  4. 如果一个节点是红色的,则它的两个子节点都是黑色的。
  5. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。

这些性质的约束使得红黑树的高度始终保持在 O (log n),从而保证了基本操作的高效性。

红黑树的操作

红黑树的基本操作包括插入、删除和查找。由于红黑树是二叉搜索树的一种,其查找操作与普通二叉搜索树相同,但插入和删除操作后需要通过旋转和变色来维持红黑树的性质。

插入操作

插入操作首先按照二叉搜索树的方式插入新节点,并将其着色为红色。然后通过一系列的旋转和颜色调整来修复红黑树的性质。插入操作的时间复杂度为 O (log n)。

插入修复主要分为三种情况:

  1. 情况 1:插入节点的父节点是红色,且叔叔节点(父节点的兄弟节点)也是红色。此时将父节点和叔叔节点变为黑色,祖父节点变为红色,然后继续处理祖父节点。
  2. 情况 2:插入节点的父节点是红色,叔叔节点是黑色,且插入节点是父节点的右孩子。此时进行左旋操作,将问题转化为情况 3。
  3. 情况 3:插入节点的父节点是红色,叔叔节点是黑色,且插入节点是父节点的左孩子。此时将父节点变为黑色,祖父节点变为红色,然后进行右旋操作。
删除操作

删除操作同样首先按照二叉搜索树的方式删除节点,然后通过旋转和变色来修复红黑树的性质。删除操作的时间复杂度也为 O (log n)。

删除修复比插入修复更为复杂,主要分为四种情况,需要考虑兄弟节点的颜色以及兄弟节点子节点的颜色。

红黑树的 C++ 实现

下面是红黑树的 C++ 实现代码,包括节点结构、插入、删除、查找等基本操作:

cpp 复制代码
#include "red_black_tree.hpp"
#include <iostream>
#include <cassert>

int main() {
    RedBlackTree<int> tree;

    // 测试插入操作
    tree.insert(10);
    tree.insert(20);
    tree.insert(5);
    tree.insert(15);
    tree.insert(30);

    std::cout << "Inorder traversal after insertion: ";
    tree.inorder(); // 应输出 5 10 15 20 30

    // 测试查找操作
    assert(tree.contains(15) == true);
    assert(tree.contains(25) == false);
    std::cout << "Contains test passed." << std::endl;

    // 测试删除操作
    tree.remove(20);
    std::cout << "Inorder traversal after deleting 20: ";
    tree.inorder(); // 应输出 5 10 15 30
    assert(tree.contains(20) == false);

    // 测试大小
    assert(tree.size() == 4);
    std::cout << "Size after deletion: " << tree.size() << std::endl;

    // 测试空树
    RedBlackTree<int> emptyTree;
    assert(emptyTree.empty() == true);
    std::cout << "Empty tree test passed." << std::endl;

    std::cout << "All tests passed!" << std::endl;

    return 0;
}    
cpp 复制代码
#ifndef RED_BLACK_TREE_HPP
#define RED_BLACK_TREE_HPP

#include <iostream>
#include <memory>
#include <functional>
#include <cassert>

template<typename T, typename Compare = std::less<T>>
class RedBlackTree {
private:
    enum class Color { RED, BLACK };

    struct Node {
        T data;
        Color color;
        std::unique_ptr<Node> left;
        std::unique_ptr<Node> right;
        Node* parent;

        Node(const T& value, Color nodeColor = Color::RED)
            : data(value), color(nodeColor), left(nullptr), right(nullptr), parent(nullptr) {}
    };

    std::unique_ptr<Node> root;
    Compare compare;
    size_t treeSize;

    // 左旋操作
    void leftRotate(Node* x) {
        Node* y = x->right.get();
        x->right = std::move(y->left);
        if (y->left) {
            y->left->parent = x;
        }
        y->parent = x->parent;

        if (!x->parent) {
            root = std::unique_ptr<Node>(y);
        } else if (x == x->parent->left.get()) {
            x->parent->left = std::unique_ptr<Node>(y);
        } else {
            x->parent->right = std::unique_ptr<Node>(y);
        }

        y->left = std::move(x->right);
        x->parent = y;
    }

    // 右旋操作
    void rightRotate(Node* y) {
        Node* x = y->left.get();
        y->left = std::move(x->right);
        if (x->right) {
            x->right->parent = y;
        }
        x->parent = y->parent;

        if (!y->parent) {
            root = std::unique_ptr<Node>(x);
        } else if (y == y->parent->right.get()) {
            y->parent->right = std::unique_ptr<Node>(x);
        } else {
            y->parent->left = std::unique_ptr<Node>(x);
        }

        x->right = std::move(y->left);
        y->parent = x;
    }

    // 插入修复
    void insertFixup(Node* z) {
        while (z->parent && z->parent->color == Color::RED) {
            if (z->parent == z->parent->parent->left.get()) {
                Node* y = z->parent->parent->right.get();
                if (y && y->color == Color::RED) {
                    z->parent->color = Color::BLACK;
                    y->color = Color::BLACK;
                    z->parent->parent->color = Color::RED;
                    z = z->parent->parent;
                } else {
                    if (z == z->parent->right.get()) {
                        z = z->parent;
                        leftRotate(z);
                    }
                    z->parent->color = Color::BLACK;
                    z->parent->parent->color = Color::RED;
                    rightRotate(z->parent->parent);
                }
            } else {
                Node* y = z->parent->parent->left.get();
                if (y && y->color == Color::RED) {
                    z->parent->color = Color::BLACK;
                    y->color = Color::BLACK;
                    z->parent->parent->color = Color::RED;
                    z = z->parent->parent;
                } else {
                    if (z == z->parent->left.get()) {
                        z = z->parent;
                        rightRotate(z);
                    }
                    z->parent->color = Color::BLACK;
                    z->parent->parent->color = Color::RED;
                    leftRotate(z->parent->parent);
                }
            }
        }
        root->color = Color::BLACK;
    }

    // 查找最小节点
    Node* minimum(Node* node) const {
        while (node->left) {
            node = node->left.get();
        }
        return node;
    }

    // 查找最大节点
    Node* maximum(Node* node) const {
        while (node->right) {
            node = node->right.get();
        }
        return node;
    }

    // 查找后继节点
    Node* successor(Node* node) const {
        if (node->right) {
            return minimum(node->right.get());
        }
        Node* y = node->parent;
        while (y && node == y->right.get()) {
            node = y;
            y = y->parent;
        }
        return y;
    }

    // 查找前驱节点
    Node* predecessor(Node* node) const {
        if (node->left) {
            return maximum(node->left.get());
        }
        Node* y = node->parent;
        while (y && node == y->left.get()) {
            node = y;
            y = y->parent;
        }
        return y;
    }

    // 删除修复
    void deleteFixup(Node* x, Node* parent) {
        while (x != root.get() && (x == nullptr || x->color == Color::BLACK)) {
            if (x == parent->left.get()) {
                Node* w = parent->right.get();
                if (w->color == Color::RED) {
                    w->color = Color::BLACK;
                    parent->color = Color::RED;
                    leftRotate(parent);
                    w = parent->right.get();
                }
                if ((w->left == nullptr || w->left->color == Color::BLACK) &&
                    (w->right == nullptr || w->right->color == Color::BLACK)) {
                    w->color = Color::RED;
                    x = parent;
                    parent = x->parent;
                } else {
                    if (w->right == nullptr || w->right->color == Color::BLACK) {
                        if (w->left) w->left->color = Color::BLACK;
                        w->color = Color::RED;
                        rightRotate(w);
                        w = parent->right.get();
                    }
                    w->color = parent->color;
                    parent->color = Color::BLACK;
                    if (w->right) w->right->color = Color::BLACK;
                    leftRotate(parent);
                    x = root.get();
                    parent = nullptr;
                }
            } else {
                Node* w = parent->left.get();
                if (w->color == Color::RED) {
                    w->color = Color::BLACK;
                    parent->color = Color::RED;
                    rightRotate(parent);
                    w = parent->left.get();
                }
                if ((w->right == nullptr || w->right->color == Color::BLACK) &&
                    (w->left == nullptr || w->left->color == Color::BLACK)) {
                    w->color = Color::RED;
                    x = parent;
                    parent = x->parent;
                } else {
                    if (w->left == nullptr || w->left->color == Color::BLACK) {
                        if (w->right) w->right->color = Color::BLACK;
                        w->color = Color::RED;
                        leftRotate(w);
                        w = parent->left.get();
                    }
                    w->color = parent->color;
                    parent->color = Color::BLACK;
                    if (w->left) w->left->color = Color::BLACK;
                    rightRotate(parent);
                    x = root.get();
                    parent = nullptr;
                }
            }
        }
        if (x) x->color = Color::BLACK;
    }

    // 中序遍历辅助函数
    void inorderTraversal(Node* node) const {
        if (node) {
            inorderTraversal(node->left.get());
            std::cout << node->data << " ";
            inorderTraversal(node->right.get());
        }
    }

    // 查找节点辅助函数
    Node* findNode(const T& value) const {
        Node* current = root.get();
        while (current) {
            if (compare(value, current->data)) {
                current = current->left.get();
            } else if (compare(current->data, value)) {
                current = current->right.get();
            } else {
                return current;
            }
        }
        return nullptr;
    }

public:
    RedBlackTree() : root(nullptr), compare(), treeSize(0) {}

    // 插入操作
    void insert(const T& value) {
        Node* z = new Node(value);
        Node* y = nullptr;
        Node* x = root.get();

        while (x) {
            y = x;
            if (compare(z->data, x->data)) {
                x = x->left.get();
            } else {
                x = x->right.get();
            }
        }

        z->parent = y;
        if (!y) {
            root = std::unique_ptr<Node>(z);
        } else if (compare(z->data, y->data)) {
            y->left = std::unique_ptr<Node>(z);
        } else {
            y->right = std::unique_ptr<Node>(z);
        }

        z->color = Color::RED;
        insertFixup(z);
        treeSize++;
    }

    // 删除操作
    bool remove(const T& value) {
        Node* z = findNode(value);
        if (!z) return false;

        Node* y = z;
        Node* x;
        Color yOriginalColor = y->color;

        if (!z->left) {
            x = z->right.get();
            transplant(z, std::move(z->right));
        } else if (!z->right) {
            x = z->left.get();
            transplant(z, std::move(z->left));
        } else {
            y = minimum(z->right.get());
            yOriginalColor = y->color;
            x = y->right.get();

            if (y->parent == z) {
                if (x) x->parent = y;
            } else {
                transplant(y, std::move(y->right));
                y->right = std::move(z->right);
                y->right->parent = y;
            }

            transplant(z, std::move(std::unique_ptr<Node>(y)));
            y->left = std::move(z->left);
            y->left->parent = y;
            y->color = z->color;
        }

        if (yOriginalColor == Color::BLACK) {
            deleteFixup(x, y->parent);
        }

        treeSize--;
        return true;
    }

    // 查找操作
    bool contains(const T& value) const {
        return findNode(value) != nullptr;
    }

    // 获取元素数量
    size_t size() const {
        return treeSize;
    }

    // 判断是否为空
    bool empty() const {
        return treeSize == 0;
    }

    // 中序遍历
    void inorder() const {
        inorderTraversal(root.get());
        std::cout << std::endl;
    }

    // 移植辅助函数
    void transplant(Node* u, std::unique_ptr<Node> v) {
        Node* uParent = u->parent;
        if (!uParent) {
            root = std::move(v);
        } else if (u == uParent->left.get()) {
            uParent->left = std::move(v);
        } else {
            uParent->right = std::move(v);
        }
        if (v) {
            v->parent = uParent;
        }
    }
};

#endif // RED_BLACK_TREE_HPP    

这段代码实现了一个模板类 RedBlackTree,包含了红黑树的基本操作。代码中使用了智能指针管理内存,确保内存安全。同时,通过插入修复和删除修复函数来维护红黑树的性质。

红黑树的应用

红黑树在计算机科学中有广泛的应用,主要包括:

  1. C++ 标准库:STL 中的 map 和 set 就是基于红黑树实现的,保证了元素的有序性和高效的插入、删除、查找操作。
  2. 操作系统:Linux 内核中的完全公平调度器(CFS)使用红黑树来管理进程调度。
  3. 数据库系统:许多数据库系统使用红黑树来索引数据,提高查询效率。
  4. 其他应用:如 Java 的 TreeMap 和 TreeSet、Python 的 SortedContainers 等。
红黑树与其他数据结构的比较

红黑树与其他平衡二叉搜索树(如 AVL 树、B 树等)相比,具有以下特点:

  1. 与 AVL 树相比:红黑树的平衡性要求不如 AVL 树严格,因此插入和删除操作的旋转次数更少,但查找操作的效率略低。
  2. 与 B 树相比:红黑树是二叉树,而 B 树是多叉树,更适合存储在磁盘等外部存储设备上。
  3. 与哈希表相比:红黑树可以保证元素的有序性,而哈希表不能,但哈希表的平均查找时间复杂度为 O (1),比红黑树更快。
总结

红黑树作为一种重要的数据结构,凭借其良好的平衡性和高效的操作性能,在计算机科学领域得到了广泛应用。通过本文的介绍,读者应该对红黑树的原理、实现和应用有了全面的了解。掌握红黑树不仅有助于理解各种高级算法和数据结构,也能在实际编程中发挥重要作用。

希望本文对您理解 C++ 红黑树有所帮助!

附:恩师博客hnjzsyjyj-CSDN博客