红黑树概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
红黑树的性质
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的,树中没有连续的红节点
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径中节点个数的两倍?
极限最短:全黑
极限最长:一黑一红
红黑树结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| enum Color { RED, BLACK };
template <class K, class V> struct RBTreeNode { RBTreeNode<K, V> *_parent; RBTreeNode<K, V> *_left; RBTreeNode<K, V> *_right; pair<K, V> _kv; Color _col;
RBTreeNode(const pair<K, V> &kv) : _parent(nullptr), _left(nullptr), _right(nullptr), _kv(kv), _col(RED) { } };
template <class K, class V> class RBTree { typedef RBTreeNode<K, V> Node;
public: bool insert(const pair<K, V> &kv) { } private: Node *_root = nullptr; };
|
红黑树操作
插入
红黑树的叔叔是关键
u存在且为红,变色继续向上处理
u不存在或存在且为黑,旋转(单旋+双旋)+变色
情况一:cur为红,parent为红,grandfather为黑(固定),uncle存在且为红
处理:p、u变黑,g变红,继续把g当成cur
情况二:cur为红,parent为红,grandfather为黑(固定),u不存在/u存在且为黑(单旋+变色)
处理:
说明:uncle的情况有两种
- 如果u节点不存在,那么cur一定是新插入节点,因为如果cur不是新插入节点,则cur和p一定有个节点颜色是黑色,就不满足性质4:每条路径黑色节点的个数相同。
- 如果u节点存在,那么cur节点原来的颜色一定是黑色的(保证性质4),现在看到其是红色的原因是因为cur的子树在调整的过程中将cur节点的颜色由黑色改为红色。
u不存在
u存在且为黑
情况三:cur为红,parent为红,grandfather为黑(固定),u不存在/u存在且为黑(双旋+变色)
u不存在
u存在且为黑
插入代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
| bool insert(const pair<K, V> &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); if (parent->_kv.first < kv.first) { parent->_right = cur; } else { parent->_left = cur; } cur->_parent = parent;
while (parent && parent->_col == RED) { Node *grandfather = parent->_parent; assert(grandfather); assert(grandfather->_col = BLACK);
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 { if (cur == parent->_left) { RotateR(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else { RotateL(parent); RotateR(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } else { Node *grandfather = parent->_parent; assert(grandfather); assert(grandfather->_col = BLACK);
Node *uncle = grandfather->_left;
if (uncle && uncle->_col == RED) { parent->_col = uncle->_col = BLACK; grandfather->_col = RED;
cur = grandfather; parent = cur->_parent; } else { if (cur == parent->_right) { RotateL(grandfather); parent->_col = BLACK; grandfather->_col = RED; } else { RotateR(parent); RotateL(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } } _root->_col = BLACK; return true; }
|
检查
- 根是黑色
- 没有连续的红节点
- 每条路径的黑色节点数量相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| bool is_balance() { if (_root == nullptr) { return true; }
if (_root->_col == RED) { cout << "根节点不是黑色" << endl; return false; } int bench_mark = 0;
return prev_check(_root, 0, bench_mark); }
bool prev_check(Node *root, int bnum, int &bench_mark) { if (root == nullptr) { if (bench_mark == 0) { bench_mark = bnum; return true; } if (bench_mark != bnum) { cout << "某条黑色节点的数量不相等" << endl; return false; } else { return true; } } if (root->_col == BLACK) { ++bnum; } if (root->_col == RED && root->_parent->_col == RED) { cout << "存在连续的红色节点" << endl; return false; } return prev_check(root->_left, bnum, bench_mark) && prev_check(root->_right, bnum, bench_mark); }
|