红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
由于从一个节点起的每条路径中的黑色节点数目相同,因此最短为全黑,同时由于红节点不连续,因此最长为一半红一半黑,因此不会超出两倍
三叉链,key-value模型、额外的标明颜色的变量(颜色可以使用枚举常量)
同时,为了在插入时遵循第4条准则,我们将颜色变量的默认值设为红色
enum Colour
{RED,BLACK
};template
struct RBTreeNode
{RBTreeNode* _left;RBTreeNode* _right;RBTreeNode* _parent;pair _kv;Colour _col;RBTreeNode(const pair& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _col(RED), _kv(kv){}
};
template
struct RBTree
{typedef RBTreeNode Node;
public:RBTree():_root(nullptr){}
private:Node* _root;
};
首先,一开始与AVL树是一致的,都是通过比较key来寻找合适的位置进行插入
bool Insert(const pair& kv)
{if (!_root){_root = new Node(kv);_root->_col = BLACK;return false;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first == kv.first)return false;else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{parent = cur;cur = cur->_left;}}cur = new Node(kv);if (parent->_kv.first < kv.first)parent->_right = cur;elseparent->_left = cur;cur->_parent = parent;
}
之后,虽然我们插入红色节点会使得每条路径的黑节点数目相同,但还要注意在红节点连续时进行相应的调整
在调整时,依旧是从插入节点(cur)向上迭代来观察,而我们需要进行调整的情况为cur和parent的颜色都为红,主要分为这两种情况
同样,和AVL树一样,也有对称的情况,但操作方法也都是类似的,不多说了
而这两种情况,我们对子树4的情况进行细分,首先当节点1的右节点为红色时
处理方法都是一样的,只需要将节点2、4的颜色改为黑色,同时,我们在改变结构时,为了减小对上面的影响,应该保证前后的路径中黑节点的数目相同,改色之前,黑节点数目为n+1,因此,我们还需要将节点1改为红色
而由于我们将节点1的颜色改为了红色,我们需要继续向上判断
while (parent&&parent->_col == RED)
{Node* par_parent = parent->_parent;Node* uncle = nullptr;if (parent == par_parent->_left)uncle = par_parent->_right;elseuncle = par_parent->_left;if (uncle && uncle->_col == RED){uncle->_col = BLACK;parent->_col = BLACK;par_parent->_col = RED;cur = par_parent;parent = cur->_parent;}
}
还有两种情况,是节点4不存在或节点4为黑色,这两种情况的处理情况相同,由于不需要对节点4进行处理,我们就简化一下
首先是情况1
首先若是我们想要改变颜色,首先对于节点2,右侧路径黑节点为n个,因此左侧也应该为n个,所以节点3的颜色不能改变,因此我们只能改变节点2,这样对于节点1,两侧路径的黑节点数目就不可能相同了,因此,我们可以使用在AVL树中使用到的旋转,很容易看出,我们需要使用右旋(对称情况使用左旋)
之后,便可以来改变颜色,首先,有两种方案,节点3改为黑色或节点1改为红色、节点2改为黑色,原本路径中黑节点数为n+1,这两种方案改变后都符合,但由于若是我们将节点2颜色改为黑色,与改变前类似,也就不需要继续向上判断了,因此采用第二种方法
else
{if (cur == parent->_left && parent == par_parent->_left){RotateR(par_parent);parent->_col = BLACK;par_parent->_col = RED;}else if (cur == parent->_right && parent == par_parent->_right){RotateL(par_parent);parent->_col = BLACK;par_parent->_col = RED;}break;
}
而左旋和右旋和AVL树是一样的
void RotateL(Node* parent)
{Node* par_parent = parent->_parent;Node* right = parent->_right;Node* right_left = right->_left;if (par_parent){if (parent == par_parent->_left)par_parent->_left = right;elsepar_parent->_right = right;}else{_root = right;}right->_parent = par_parent;if (right_left)right_left->_parent = parent;parent->_right = right_left;right->_left = parent;parent->_parent = right;
}void RotateR(Node* parent)
{Node* par_parent = parent->_parent;Node* left = parent->_left;Node* left_right = left->_right;if (par_parent){if (parent == par_parent->_left)par_parent->_left = left;elsepar_parent->_right = left;}else{_root = left;}left->_parent = par_parent;if (left_right)left_right->_parent = parent;parent->_left = left_right;left->_right = parent;parent->_parent = left;
}
之后便是情况2
同样只是改变颜色无法实现平衡
我们就需要进行双旋
之后就需要改变颜色
还是将节点3变黑,节点1变红,不需要向上调整
else
{if (cur == parent->_left && parent == par_parent->_left){RotateR(par_parent);parent->_col = BLACK;par_parent->_col = RED;}else if (cur == parent->_left && parent == par_parent->_right){RotateR(parent);RotateL(par_parent);cur->_col = BLACK;par_parent->_col = RED;}else if (cur == parent->_right && parent == par_parent->_right){RotateL(par_parent);parent->_col = BLACK;par_parent->_col = RED;}else{RotateL(parent);RotateR(par_parent);cur->_col = BLACK;par_parent->_col = RED;}break;
}
而在最后,头结点可能会被我们改变为红色,我们只需要将其手动变为黑就好了,不会对下面产生影响
总览
bool Insert(const pair& kv)
{if (!_root){_root = new Node(kv);_root->_col = BLACK;return false;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first == kv.first)return false;else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{parent = cur;cur = cur->_left;}}cur = new Node(kv);if (parent->_kv.first < kv.first)parent->_right = cur;elseparent->_left = cur;cur->_parent = parent;while (parent&&parent->_col == RED){Node* par_parent = parent->_parent;Node* uncle = nullptr;if (parent == par_parent->_left)uncle = par_parent->_right;elseuncle = par_parent->_left;if (uncle && uncle->_col == RED){uncle->_col = BLACK;parent->_col = BLACK;par_parent->_col = RED;cur = par_parent;parent = cur->_parent;}else{if (cur == parent->_left && parent == par_parent->_left){RotateR(par_parent);parent->_col = BLACK;par_parent->_col = RED;}else if (cur == parent->_left && parent == par_parent->_right){RotateR(parent);RotateL(par_parent);cur->_col = BLACK;par_parent->_col = RED;}else if (cur == parent->_right && parent == par_parent->_right){RotateL(par_parent);parent->_col = BLACK;par_parent->_col = RED;}else{RotateL(parent);RotateR(par_parent);cur->_col = BLACK;par_parent->_col = RED;}break;}}_root->_col = BLACK;return true;
}void RotateL(Node* parent)
{Node* par_parent = parent->_parent;Node* right = parent->_right;Node* right_left = right->_left;if (par_parent){if (parent == par_parent->_left)par_parent->_left = right;elsepar_parent->_right = right;}else{_root = right;}right->_parent = par_parent;if (right_left)right_left->_parent = parent;parent->_right = right_left;right->_left = parent;parent->_parent = right;
}void RotateR(Node* parent)
{Node* par_parent = parent->_parent;Node* left = parent->_left;Node* left_right = left->_right;if (par_parent){if (parent == par_parent->_left)par_parent->_left = left;elsepar_parent->_right = left;}else{_root = left;}left->_parent = par_parent;if (left_right)left_right->_parent = parent;parent->_left = left_right;left->_right = parent;parent->_parent = left;
}
首先就是验证是否为搜索树,和AVL树的一致
void InOrder()
{_InOrder(_root);cout << endl;
}void _InOrder(Node* root)
{if (root == NULL)return;_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second <<':' << root->_col << " ";_InOrder(root->_right);
}
之后,还需要验证三个问题:根节点是否为黑,路径中的黑节点数目是否相同、红节点是否不连续
首先第一点很好验证,若是验证第二点,我们可以先得到一条路径的黑节点,之后以它为基准,对每条路径进行判断,最后在遍历路径的时候顺便对红节点是否连续做一下判断
bool IsBalance()
{if (_root && _root->_col == RED){cout << "根节点不是黑色" << endl;return false;}int banchmark = 0;Node* left = _root;while (left){if (left->_col == BLACK)banchmark++;left = left->_left;}int blackNum = 0;return _IsBalance(_root, banchmark, blackNum);
}bool _IsBalance(Node* root, int& banchmark, int blackNum)
{if (!root){if (banchmark != blackNum){cout << "存在路径黑色节点的数量不相等" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << "出现连续红色节点" << endl;return false;}if (root->_col == BLACK){++blackNum;}return _IsBalance(root->_left, banchmark, blackNum)&& _IsBalance(root->_right, banchmark, blackNum);
}
end