C++ 红黑树

文章目录

红黑树

红黑树的概念

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

红黑树的性质

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

上述5点性质是构建红黑树的必要条件,那么为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?

我们设最短路径黑色节点数为 bh。

  • 最短路径可以全是黑色,长度为 bh。因为根节点是黑色且叶子都是黑色,但是并没有规定非叶子节点不能为黑色,那么最短路径就可以全是黑色。
  • 最长路径可以红黑交替,因为它可以连续黑,但不能有连续红节点,且黑色节点数也为 bh。
  • 最长路径最多有 bh 个黑节点和 bh 个红节点,总长度最多 2bh。
  • 所以最长路径 <= 2 * 最短路径。

红黑树节点的定义

cpp 复制代码
enum Color { RED, BLACK };

template<class ValueType>
struct RBTreeNode {
    RBTreeNode(const ValueType& data = ValueType(), Color color = RED)
        : _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr),
          _data(data), _color(color) {}

    RBTreeNode<ValueType>* _pLeft;
    RBTreeNode<ValueType>* _pRight;
    RBTreeNode<ValueType>* _pParent;
    ValueType _data;
    Color _color;
};

红黑树结构

这里节点的定义是为了后续实现关联式容器简单,给红黑树实现增加一个头节点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点,如下:

红黑树的插入操作

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

  1. 按二叉搜索树(BST)规则插入
    • 如果是空树,新节点作为根节点,直接涂黑。
    • 如果非空,按BST规则找到位置插入,默认涂红。
    • 插入后,检查是否破坏了红黑树性质。如果父节点是黑色,则无需调整;如果父节点是红色,则违反了性质3,需要进行调整。

为什么非空插入节点默认涂红?

因为如果插入黑色节点,一定会改变某条路径的黑色节点数量,违反性质 4,几乎必须调整。

如果插入红色节点,只有父节点也是红色时才违反性质 3,调整概率更低,且调整更简单。

  1. 红黑树的平衡调整
    约定: cur 为当前节点,p 为父节点,g 为祖父节点,u 为叔叔节点(p的兄弟节点)。
    触发调整的条件: p 为红,g 必为黑(因为不能有连续红节点)。
    根据 u 的颜色和 cur 的位置,分为三种情况:
    • 情况一:叔叔 u 存在且为红

      • 特征:cur 红,p 红,g 黑,u 红。

      • 需注意下图可能是完整的树,也可能是一颗子树

      • 处理方式(只变色,不旋转):

        • 将 p 变黑。
        • 将 u 变黑。
        • 将 g 变红。
      • 后续:将 g 作为新的 cur,继续向上调整。如果 g 是根节点,最后需强制变黑。

      • 为什么这么做? 如果只把 p 改黑,会导致 p 所在路径黑节点数+1,破坏性质4。所以必须把 u 也改黑,再让 g 变红,保持整体黑高不变。

    • 情况二:叔叔 u 不存在 或 u 存在且为黑,且 cur、p、g 在同一条直线上(共线)

      • 特征: p 红,u 黑/无,g 黑。cur 和 p 同为左孩子或同为右孩子。

      • 处理方式(旋转 + 变色):

        • 若 p 是 g 的左孩子 ,cur 是 p 的左孩子 :对 g 进行右单旋。
        • 若 p 是 g 的右孩子 ,cur 是 p 的右孩子 :对 g 进行左单旋。
      • **变色:**旋转后,将 p 变黑,g 变红。

      • **注意:**此时局部根节点变为 p(黑),且解决了连续红节点的问题。

    • 情况三:叔叔 u 不存在 或 u 存在且为黑,且 cur、p、g 不在同一条直线上(折线)

      • 特征: p 红,u 黑/无,g 黑。cur 和 p 一个是左孩子,一个是右孩子。

      • 处理方式(双旋 + 变色):

        • 若 p 是 g 的左孩子 ,cur 是 p 的右孩子 :对 p 进行左单旋。
        • 若 p 是 g 的右孩子 ,cur 是 p 的左孩子 :对 p 进行右单旋。
      • 后续: 旋转后,交换 cur 和 p 的指针 。此时结构变成了情况二 (共线),然后再按照情况二进行处理(对 g 旋转并变色)

cpp 复制代码
bool Insert(const ValueType& data) {
    Node*& pRoot = GetRoot();

    if (pRoot == nullptr) {
        pRoot = new Node(data, BLACK);
        pRoot->_pParent = _pHead;
        _pHead->_pParent = pRoot;
        _pHead->_pLeft = pRoot;
        _pHead->_pRight = pRoot;
        return true;
    }

    Node* parent = nullptr;
    Node* cur = pRoot;

    while (cur) {
        if (data < cur->_data) {
            parent = cur;
            cur = cur->_pLeft;
        }
        else if (data > cur->_data) {
            parent = cur;
            cur = cur->_pRight;
        }
        else {
            return false;
        }
    }

    cur = new Node(data, RED);
    cur->_pParent = parent;

    if (data < parent->_data)
        parent->_pLeft = cur;
    else
        parent->_pRight = cur;

    while (parent && parent->_color == RED) {
        Node* grandFather = parent->_pParent;

        if (parent == grandFather->_pLeft) {
            Node* uncle = grandFather->_pRight;

            if (uncle && uncle->_color == RED) {
                parent->_color = BLACK;
                uncle->_color = BLACK;
                grandFather->_color = RED;
                cur = grandFather;
                parent = cur->_pParent;
            }
            else {
                if (cur == parent->_pRight) {
                    RotateL(parent);
                    swap(parent, cur);
                }
                grandFather->_color = RED;
                parent->_color = BLACK;
                RotateR(grandFather);
                break;
            }
        }
        else {
            Node* uncle = grandFather->_pLeft;

            if (uncle && uncle->_color == RED) {
                parent->_color = BLACK;
                uncle->_color = BLACK;
                grandFather->_color = RED;
                cur = grandFather;
                parent = cur->_pParent;
            }
            else {
                if (cur == parent->_pLeft) {
                    RotateR(parent);
                    swap(parent, cur);
                }
                grandFather->_color = RED;
                parent->_color = BLACK;
                RotateL(grandFather);
                break;
            }
        }
    }

    pRoot->_color = BLACK;
    _pHead->_pLeft = LeftMost();
    _pHead->_pRight = RightMost();
    return true;
}

红黑树的验证

红黑树的检测分为两步:

  1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
  2. 检测其是否满足红黑树的性质
cpp 复制代码
bool IsValidRBTree() {
    Node* pRoot = GetRoot();
    if (pRoot == nullptr) return true;

    if (pRoot->_color != BLACK) {
        cout << "违反性质2:根不是黑色" << endl;
        return false;
    }

    size_t blackCount = 0;
    Node* cur = pRoot;
    while (cur) {
        if (cur->_color == BLACK) ++blackCount;
        cur = cur->_pLeft;
    }

    size_t k = 0;
    return _IsValidRBTree(pRoot, k, blackCount);
}

bool _IsValidRBTree(Node* root, size_t k, size_t blackCount) {
    if (root == nullptr) {
        if (k != blackCount) {
            cout << "违反性质4:黑高不同" << endl;
            return false;
        }
        return true;
    }

    if (root->_color == BLACK) ++k;

    Node* parent = root->_pParent;
    if (parent && parent->_color == RED && root->_color == RED) {
        cout << "违反性质3:红红相连" << endl;
        return false;
    }

    return _IsValidRBTree(root->_pLeft, k, blackCount) &&
        _IsValidRBTree(root->_pRight, k, blackCount);
}

红黑树完整代码

cpp 复制代码
#pragma once
#include <iostream>
#include <utility>

using namespace std;

enum Color { RED, BLACK };

template<class ValueType>
struct RBTreeNode {
    RBTreeNode(const ValueType& data = ValueType(), Color color = RED)
        : _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr),
        _data(data), _color(color) {
    }

    RBTreeNode<ValueType>* _pLeft;
    RBTreeNode<ValueType>* _pRight;
    RBTreeNode<ValueType>* _pParent;
    ValueType _data;
    Color _color;
};

template<class ValueType>
class RBTree {
    typedef RBTreeNode<ValueType> Node;
public:
    RBTree() {
        _pHead = new Node;
        _pHead->_color = BLACK;
        _pHead->_pLeft = _pHead;
        _pHead->_pRight = _pHead;
        _pHead->_pParent = nullptr;
    }

    ~RBTree()
    {
        Clear();
        delete _pHead;
    }

    bool Insert(const ValueType& data) {
        Node*& pRoot = GetRoot();

        if (pRoot == nullptr) {
            pRoot = new Node(data, BLACK);
            pRoot->_pParent = _pHead;
            _pHead->_pParent = pRoot;
            _pHead->_pLeft = pRoot;
            _pHead->_pRight = pRoot;
            return true;
        }

        Node* parent = nullptr;
        Node* cur = pRoot;

        while (cur) {
            if (data < cur->_data) {
                parent = cur;
                cur = cur->_pLeft;
            }
            else if (data > cur->_data) {
                parent = cur;
                cur = cur->_pRight;
            }
            else {
                return false;
            }
        }

        cur = new Node(data, RED);
        cur->_pParent = parent;

        if (data < parent->_data)
            parent->_pLeft = cur;
        else
            parent->_pRight = cur;

        while (parent && parent->_color == RED) {
            Node* grandFather = parent->_pParent;

            if (parent == grandFather->_pLeft) {
                Node* uncle = grandFather->_pRight;

                if (uncle && uncle->_color == RED) {
                    parent->_color = BLACK;
                    uncle->_color = BLACK;
                    grandFather->_color = RED;
                    cur = grandFather;
                    parent = cur->_pParent;
                }
                else {
                    if (cur == parent->_pRight) {
                        RotateL(parent);
                        swap(parent, cur);
                    }
                    grandFather->_color = RED;
                    parent->_color = BLACK;
                    RotateR(grandFather);
                    break;
                }
            }
            else {
                Node* uncle = grandFather->_pLeft;

                if (uncle && uncle->_color == RED) {
                    parent->_color = BLACK;
                    uncle->_color = BLACK;
                    grandFather->_color = RED;
                    cur = grandFather;
                    parent = cur->_pParent;
                }
                else {
                    if (cur == parent->_pLeft) {
                        RotateR(parent);
                        swap(parent, cur);
                    }
                    grandFather->_color = RED;
                    parent->_color = BLACK;
                    RotateL(grandFather);
                    break;
                }
            }
        }

        pRoot->_color = BLACK;
        _pHead->_pLeft = LeftMost();
        _pHead->_pRight = RightMost();
        return true;
    }

    bool IsValidRBTree() {
        Node* pRoot = GetRoot();
        if (pRoot == nullptr) return true;

        if (pRoot->_color != BLACK) {
            cout << "违反性质2:根不是黑色" << endl;
            return false;
        }

        size_t blackCount = 0;
        Node* cur = pRoot;
        while (cur) {
            if (cur->_color == BLACK) ++blackCount;
            cur = cur->_pLeft;
        }

        size_t k = 0;
        return _IsValidRBTree(pRoot, k, blackCount);
    }
private:
    Node*& GetRoot() { return _pHead->_pParent; }

    Node* LeftMost() {
        Node* cur = GetRoot();
        if (!cur) return _pHead;
        while (cur->_pLeft) cur = cur->_pLeft;
        return cur;
    }

    Node* RightMost() {
        Node* cur = GetRoot();
        if (!cur) return _pHead;
        while (cur->_pRight) cur = cur->_pRight;
        return cur;
    }

    void RotateL(Node* parent) {
        Node* subR = parent->_pRight;
        Node* subRL = subR->_pLeft;

        parent->_pRight = subRL;
        if (subRL) subRL->_pParent = parent;

        subR->_pLeft = parent;
        Node* ppNode = parent->_pParent;
        parent->_pParent = subR;
        subR->_pParent = ppNode;

        if (ppNode == _pHead)
            _pHead->_pParent = subR;
        else if (ppNode->_pLeft == parent)
            ppNode->_pLeft = subR;
        else
            ppNode->_pRight = subR;
    }

    void RotateR(Node* parent) {
        Node* subL = parent->_pLeft;
        Node* subLR = subL->_pRight;

        parent->_pLeft = subLR;
        if (subLR) subLR->_pParent = parent;

        subL->_pRight = parent;
        Node* ppNode = parent->_pParent;
        parent->_pParent = subL;
        subL->_pParent = ppNode;

        if (ppNode == _pHead)
            _pHead->_pParent = subL;
        else if (ppNode->_pLeft == parent)
            ppNode->_pLeft = subL;
        else
            ppNode->_pRight = subL;
    }

    bool _IsValidRBTree(Node* root, size_t k, size_t blackCount) {
        if (root == nullptr) {
            if (k != blackCount) {
                cout << "违反性质4:黑高不同" << endl;
                return false;
            }
            return true;
        }

        if (root->_color == BLACK) ++k;

        Node* parent = root->_pParent;
        if (parent && parent->_color == RED && root->_color == RED) {
            cout << "违反性质3:红红相连" << endl;
            return false;
        }

        return _IsValidRBTree(root->_pLeft, k, blackCount) &&
            _IsValidRBTree(root->_pRight, k, blackCount);
    }

    void Clear() {
        _Clear(GetRoot());
        GetRoot() = nullptr;
        _pHead->_pLeft = _pHead;
        _pHead->_pRight = _pHead;
    }

    void _Clear(Node* root) {
        if (!root) return;
        _Clear(root->_pLeft);
        _Clear(root->_pRight);
        delete root;
    }
private:
    Node* _pHead;
};

红黑树与AVL树的比较

比较维度 AVL树 红黑树
平衡标准 严格平衡(左右子树高度差绝对值不超过1) 弱平衡(最长路径不超过最短路径的2倍)
树的高度 最低,高度约为 ( 1.44 l o g 2 N (1.44log_2 N (1.44log2N) 略高,高度约为 ( 2 l o g 2 N (2log_2 N (2log2N)
查找效率 极高(由于树更矮,查找所需比较次数更少) 较高(略逊于AVL树,但依然是 ( O ( l o g N ) (O(log N) (O(logN)))
插入/删除效率 较低(为了维持严格平衡,可能需要频繁旋转) 较高(调整次数少,只需少量的变色和最多3次旋转)
插入旋转次数 最多 1 次旋转(单旋或双旋) 最多 2 次旋转
删除旋转次数 最坏情况需要 ( O ( l o g N ) (O(log N) (O(logN)) 次旋转(一路旋转到根节点) 最多 3 次旋转
节点存储开销 需要额外存储平衡因子或高度 需要额外存储颜色
实现复杂度 插入和删除逻辑较复杂,需维护平衡因子 插入和删除逻辑(尤其是情况分支)较多,但调整较规律
适用场景 查找密集型场景(查多改少) 插入、删除频繁的场景(查改均衡或改多)
典型应用 Windows NT内核(早期)、数据库索引(部分) C++ STL (map, set, multimap, multiset)、Java TreeMap、HashMap(链表转红黑树)、Linux内核调度器/文件系统、Nginx

完

相关推荐
ZoeLandia1 小时前
TypeScript 04:类型断言与类型守卫
开发语言·前端·typescript
geovindu1 小时前
rust: pool Pattern
开发语言·后端·设计模式·rust·对象池简写模式
曼巴UE51 小时前
UE 属性同步
网络·c++·ue5
无忧.芙桃2 小时前
图的精髓:邻接矩阵与遍历全解析
c++·算法·面试·深度优先·广度优先
jimy12 小时前
标准的动态类型定义与“多态”无关,非多态对象同样会出现动态类型不同于静态类型的情形
开发语言·c++
冬、不眠的夜2 小时前
Nuitka 将 python 程序编译成 exe 指南
开发语言·python
CVer儿2 小时前
起源1引擎相关知识点2--World Brush+func_detail
开发语言
MOONICK2 小时前
COM 组件技术(三)
c++·windows
_wxd6662 小时前
二叉搜索树(BST)万字详解:概念、操作、性能分析与 C++ 实现
数据结构·c++·二叉搜索树