
红黑树是一种自平衡的二叉搜索树。它的节点被标记为红色或黑色,通过特定的规则来保持树的近似平衡(最长路径不超过最短路径的二倍)。其主要规则:
那么红黑树是如何凭借颜色来控制平衡的呢?
我们想在不违反红黑树规则,极端情况如下图【注:这种情况不一定出现,用来方便 理解】
最短的路径:全是黑色 最长的路径:一黑一红
那么我们设最短路径为n,最长路径也就是2n,即这张图中最长路径就是最短路径的2倍。
而还可以是这种情况如下图
这种情况就是最短路径的2倍大于最长路径。
所以这就是红黑树的近似平衡(即最长路径不超过最短路径的二倍)
而我们这里的最长最短路径是在红黑树规则理论上而言,实际的红黑 树中最长最短不要低估是上米娜的图所示。
红黑树之所以在众多数据结构中脱颖而出,是因为它在插入和删除操作时能够在 O(log n) 的时间复杂度内自动调整,保持平衡,从而保证了高效的查找、插入和删除性能。
以AVL树做对比:
平衡机制:
性能:
空间复杂度:
enum Color { RED, BLACK }; template struct RBTreeNode { pair _kv;//值 RBTreeNode* _left;//该节点的左孩子 RBTreeNode* _right;//该节点的右孩子 RBTreeNode* _parent;//该节点的父节点 Color _col; RBTreeNode(const pair& kv) : _kv(kv) , _left(nullptr) , _right(nullptr) , _parent(nullptr) {} }; 先定义一个枚举类型 Color 和一个模板结构体 RBTreeNode 。
枚举类型储存红黑两种颜色。
pair :用于存储键值对数据。
RBTreeNode 、RBTreeNode 和 RBTreeNode :分别指向该节点的左孩子、右孩子和父节点,构建了树结构中的节点关系。
Color _col :使用前面定义的枚举类型来表示节点的颜色属性。
结构体中的构造函数 RBTreeNode(const pair 用于初始化节点对象,将传入的键值对赋值给 _kv,并将指针 _left、_right 和 _parent 初始化为 nullptr。
首先,按照二叉搜索树的插入规则,将新节点插入到合适的位置。
新插入的节点默认是红色,原因:
如果我们插入红色节点我们破坏规则4(如果一个节点是红色的,那么它的两个子节点都是黑色的,也就是一条路径中,没有连续的红色节点)如果我们插入黑色节点我们破坏规则3(从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点)那么我们插入黑色节点的时候规则3必然会被破坏,而插入红色节点如果父节点是黑色则没有事情,如果父节点是红色才会破坏规则4==,所以相比之下插入红色节点更好。
父节点是黑色,那么无需做任何调整,树已经满足红黑树的性质。 父节点是红色,那么会出现违反红黑树性质的情况,需要进行调整。 兄弟节点是黑色,且父节点是祖父节点的左孩子兄弟节点是黑色,且父节点是祖父节点的右孩子兄弟节点是红色bool Insert(const pair& kv) { if (_root == nullptr) { _root = new Node(kv); _root->_col = BLACK; return true; } Node* parent = nullptr; Node* cur = _root; while (cur) { if (cur->_kv.first < kv.first) { parent = cur; cur = cur->_right; } else if (cur->_kv.first > kv.first) { parent = cur; cur = cur->_left; } else { return false; } } cur = new Node(kv); //新增节点给红色 cur->_col = RED; if (parent->_kv.first < kv.first) { parent->_right = cur; } else { parent->_left = cur; } cur->_parent = parent; //进行变色和旋转调整 while (parent && parent->_col == RED) { //如果u存在,cur和parent都是红,grandfather是黑 //p,u变成黑,g变成红,g当成cur向上调整 //u存在且是红 -> 变色进行调整 Node* grandfather = parent->_parent; //p在g的左边 if (parent == grandfather->_left) { Node* uncle = grandfather->_right; if (uncle&&uncle->_col == RED) { parent->_col = uncle->_col = BLACK; grandfather->_col = RED; cur = grandfather; parent = cur->_parent; } else { //u存在且为黑或者不存在 -> 旋转加变色 if (cur == parent->_left) { //单旋 // g // p u //c RotateR(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else { //双旋 // g // p u // c RotateL(parent); RotateR(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } else { //u在g的左边 // g // u p Node* uncle = grandfather->_left; //u存在且为红 -> 直接变色 if (uncle&&uncle->_col == RED) { parent->_col = uncle->_col = BLACK; grandfather->_col = RED; cur = grandfather; parent = cur->_parent; } else { //u存在且为黑或者不存在 -> 旋转加变色 if (cur == parent->_right) { //单旋 // g // u p // c RotateL(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else { //双旋 // g // u p // c RotateR(parent); RotateL(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } } _root->_col = BLACK; return true; } 红黑树在许多领域都有广泛的应用,比如:
数据库中的索引结构。
各种编程语言的标准库中,如 Java 中的 TreeMap 和 TreeSet 。
总之,红黑树作为一种高效且强大的数据结构,对于优化程序性能和提高数据管理效率具有重要意义。深入理解和掌握红黑树,将为我们在计算机科学领域的探索提供有力的支持。