红黑树(Red-Black Tree)

红黑树(Red-Black Tree)

1.前言

  • 本文中,我们使用KV实现红黑树。
  • 源码链接放在文章结尾

红黑树是一种自平衡的 二叉搜索树 (Binary Search Tree, BST),通过引入额外的规则(红黑性质)来保证树的高度接近最优,从而在最坏情况下提供O(logn) 的操作复杂度。

红黑树的增删查改都是基于二叉搜索树的,如果不了解二叉搜索树,点我进入二叉搜索树的世界

红黑树的旋转跟AVL树的旋转相同,点我进入AVL树的世界

2.红黑树的特性

红黑树在其节点上引入了红色和黑色的颜色属性,并满足以下红黑性质:

  1. 节点颜色:每个节点必须是红色或黑色。
  2. 根节点是黑色:红黑树的根节点必须是黑色。
  3. 红色节点不能相邻:红色节点的子节点必须是黑色(即不存在连续两个红色节点)。
  4. 每条路径上的黑高相等:从任意节点到其所有叶子节点(NIL 或空节点)的路径上,经过的黑色节点数必须相同。
  5. 叶子节点为黑色:红黑树中的叶子节点是一个虚拟的黑色 NIL 节点。

这些性质共同确保了红黑树的平衡,避免其退化成链表。

3.节点的定义

我们使用枚举来定义红黑树的颜色:

enum Color
{
	RED,
	BLACK
};

红黑树的颜色属性是其核心特性,用于维护树的平衡性。

template<class K, class V>
struct RBTreeNode
{
    // 左子节点
	RBTreeNode<K, V>* _left;

    // 右子节点
	RBTreeNode<K, V>* _right;

    // 父节点
	RBTreeNode<K, V>* _parent;

    // 键值对
	pair<K, V> _kv;

    // 节点颜色
	Color _col;

    // 构造函数
	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _col(RED)		// 新插入的节点默认是红色
	{}
};

思考:为什么要默认插入红色节点?

  • 如果插入的是红色节点,特性3红色节点不能相邻会被破坏。
  • 如果插入的是黑色节点,特性4每条路径上的黑高相等会被破坏。
  • 因为红色节点不能相邻被破环比每条路径上的黑高相等好恢复,所以避难就易,插入红色节点。

4.插入

插入的思路

  1. 按照二叉搜索树的规则插入。
  2. 插入后更新节点颜色,维护红黑树的规则。

更新节点颜色的思路

  1. 如果父亲的颜色为黑色,不破坏红黑树性质,不更新节点颜色。
  2. 如果父亲的颜色为红色:
    1. 如果叔叔节点存在并且为红色:
      • 将祖父节点置为红色,将父亲和叔叔节点置为黑色。
      • 向上更新
    2. 如果叔叔节点不存在,或者存在为黑色:
      1. 父亲是祖父的左孩子:
        1. 孩子是父亲的左孩子:
          • 右旋祖父
          • 祖父节点置为红色,父亲节点置为黑色
          • 停止更新
        2. 孩子是父亲的右孩子:
          • 左旋父亲
          • 交换父亲和孩子的指针
          • 右旋祖父
          • 祖父节点置为红色,父亲节点置为黑色
          • 停止更新
      2. 父亲是祖父的右孩子:
        1. 孩子是父亲的右孩子:
          • 左旋祖父
          • 祖父节点置为红色,父亲节点置为黑色
          • 停止更新
        2. 孩子是父亲的左孩子:
          • 右旋父亲
          • 交换父亲和孩子的指针
          • 左旋祖父
          • 祖父节点置为红色,父亲节点置为黑色
          • 停止更新
//代码实现
template<class K, class V>
class RBTree {
    typedef RBTreeNode<K, V> Node;

public:
    bool Insert(const pair<K, V>& kv) {
        // 1. 按二叉搜索树规则插入
        if (!_root) {
            _root = new Node(kv);
            _root->_col = BLACK;
            return true;
        }

        Node* parent = nullptr;
        Node* cur = _root;
        while (cur) {
            parent = cur;
            if (kv.first < cur->_kv.first)
                cur = cur->_left;
            else if (kv.first > cur->_kv.first)
                cur = cur->_right;
            else
                return false;
        }

        cur = new Node(kv);
        if (kv.first < parent->_kv.first)
            parent->_left = cur;
        else
            parent->_right = cur;
        cur->_parent = parent;

        // 2. 调整颜色以恢复红黑性质
        while (parent && parent->_col == RED) {
            Node* grandfather = parent->_parent;
            if (grandfather->_left == parent) {
                Node* uncle = grandfather->_right;
                if (uncle && uncle->_col == RED) {
                    // Case 1: 父节点和叔叔节点为红色
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;
                    cur = grandfather;
                    parent = cur->_parent;
                } else {
                    // Case 2 & 3: 叔叔节点为黑色或不存在
                    if (cur == parent->_right) {
                        RotateL(parent);
                        swap(parent, cur);
                    }
                    RotateR(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                    break;
                }
            } else {
                Node* uncle = grandfather->_left;
                if (uncle && uncle->_col == RED) {
                    // Case 1: 父节点和叔叔节点为红色
                    parent->_col = uncle->_col = BLACK;
                    grandfather->_col = RED;
                    cur = grandfather;
                    parent = cur->_parent;
                } else {
                    // Case 2 & 3: 叔叔节点为黑色或不存在
                    if (cur == parent->_left) {
                        RotateR(parent);
                        swap(parent, cur);
                    }
                    RotateL(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                    break;
                }
            }
        }

        _root->_col = BLACK; // 确保根节点为黑色
        return true;
    }

private:
    void RotateL(Node*& parent) {
        Node* rightChild = parent->_right;
        parent->_right = rightChild->_left;
        if (rightChild->_left)
            rightChild->_left->_parent = parent;
        rightChild->_parent = parent->_parent;
        if (!parent->_parent)
            _root = rightChild;
        else if (parent == parent->_parent->_left)
            parent->_parent->_left = rightChild;
        else
            parent->_parent->_right = rightChild;
        rightChild->_left = parent;
        parent->_parent = rightChild;
    }

    void RotateR(Node*& parent) {
        Node* leftChild = parent->_left;
        parent->_left = leftChild->_right;
        if (leftChild->_right)
            leftChild->_right->_parent = parent;
        leftChild->_parent = parent->_parent;
        if (!parent->_parent)
            _root = leftChild;
        else if (parent == parent->_parent->_left)
            parent->_parent->_left = leftChild;
        else
            parent->_parent->_right = leftChild;
        leftChild->_right = parent;
        parent->_parent = leftChild;
    }

private:
    Node* _root = nullptr;
};	

5.AVL树和红黑树的比较

对比项红黑树AVL树
树的高度相对较高,但保证 O(log⁡n)O(\log n)O(logn)。更加严格平衡,树的高度更接近最优。
插入效率通常更快,最多 O(log⁡n)O(\log n)O(logn) 次旋转,且常数因子较小。较慢,可能需要多次旋转(最多 2 次)。
删除效率通常更快,调整操作较少,常数因子较低。较慢,可能需要多次旋转(最多 O(log⁡n)O(\log n)O(logn))。
查找效率较慢,因高度可能比 AVL 稍高。较快,因为严格平衡导致树更矮。
空间效率较优,因旋转次数少、存储开销小。略差,因频繁旋转需要更多栈调用等开销。
适用场景插入、删除频繁的场景(如字典、关联容器)。查找频繁的场景(如数据库索引)。

,因为严格平衡导致树更矮。 |
| 空间效率 | 较优,因旋转次数少、存储开销小。 | 略差,因频繁旋转需要更多栈调用等开销。 |
| 适用场景 | 插入、删除频繁的场景(如字典、关联容器)。 | 查找频繁的场景(如数据库索引)。 |

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值