一、RedBlackTree class defination
template<class T>
class NodeT
{
public:
T data;
NodeT* left;
NodeT* right;
NodeT* parent;
bool isBlack; // determine whether the node is red or black. true = black, false = red
// constructor
NodeT<T>(T value) : data(value), left(nullptr), right(nullptr), parent(nullptr), isBlack(false) {;}
};
template<class T>
class RedBlackTree
{
private:
NodeT<T>* root;
int length;
// POST: finds the value in the tree and returns it node, if no such value returns nullptr
NodeT<T>* findNode(T value) const;
// POST: find the target's predecessor in the tree
NodeT<T>* getPredecessor(NodeT<T> *target) const;
// POST: binary search tree insert algorithm
bool BST_insert(NodeT<T>* newNode);
// POST: left rotate the tree along x
void leftRotate(NodeT<T> *x);
// POST: right rotate the three along x
void rightRotate(NodeT<T> *x);
// PARAM: newNode - the new node after delete a black node
// newNodeParent - the parent of new node
// isLeft - is the new node a left or right child
// POST: fix the tree after delete a black node
void rbFix(NodeT<T>* newNode, NodeT<T>* newNodeParent);
public:
// default constructor -- size = 0; root = nullptr
RedBlackTree();
// POST: if the parameter is not already in the tree, inserts it and returns true; otherwise
// does nothing and returns false
// PARAM: the value to be inserted
bool insert(T value);
// POST: if the parameter exists in tree, deletes it and returns true; otherwise returns false
// PARAM: the value to be removed
bool remove(T value);
};
二、红黑树的插入
代码如下:
*helper method都会放在文章的最下面
template<class T>
bool RedBlackTree<T>::insert(T value)
{
NodeT<T>* newNode = new NodeT<T>(value);
// if value is already in the tree; returns false
if(BST_insert(newNode) == false){
return false;
}
// if new node's parent is red
while(newNode != root && newNode->parent->isBlack == false){
NodeT<T> *uncle;
if(newNode->parent->parent->right == newNode->parent){ // new node's parent is a right child; uncle is left child
uncle = newNode->parent->parent->left;
// if new node's uncle is red, new node's parent and uncle marks with black,
// its grandparent marks with red
if(uncle != nullptr && uncle->isBlack == false){ // uncle is red
newNode->parent->isBlack = true;
uncle->isBlack = true;
newNode->parent->parent->isBlack = false;
// if new node's grandparent's parent still is red,
// continue loop until gp's parent is black
newNode = newNode->parent->parent;
}
else{ // uncle is black
// detemine new node is inline or is not inline,
// if is not inline, rotate along the parent to the inline direction;
// in this case, right is inline direction
if(newNode == newNode->parent->left){
newNode = newNode->parent;
rightRotate(newNode);
}
// left rotate along the grandparent to the uncle direction
newNode->parent->isBlack = true;
newNode->parent->parent->isBlack = false;
leftRotate(newNode->parent->parent);
}
}
else{ // uncle is a right child; mirror case
uncle = newNode->parent->parent->right;
if (uncle != nullptr && uncle->isBlack == false)
{
newNode->parent->isBlack = true;
newNode->parent->parent->isBlack = false;
uncle->isBlack = true;
newNode = newNode->parent->parent;
}
else
{
if (newNode == newNode->parent->right)
{
newNode = newNode->parent;
leftRotate(newNode);
}
newNode->parent->isBlack = true;
newNode->parent->parent->isBlack = false;
rightRotate(newNode->parent->parent);
}
}
}
root->isBlack = true;
return true;
}
1. 先用二叉树插入新的节点,新的节点标为红色
1.1如果要插入的值已经存在节点中,什么都不做,返回false
2. 当插入的节点的parent也是红色时
为了修复红色的父节点不能有红色的子节点,我们要对他进行修复。修复插入的时候我们要去看插入节点的uncle节点(父节点的兄弟)。
注意:uncle节点也可能是nullptr的叶子节点,为黑色
2.1 如果没有uncle节点,被插入的节点就是根,直接染黑。
2.2 如果uncle是红,parent染黑,grandparent染红,uncle染黑。
例子:
插入30
插入节点的parent染黑,gp染红,uncle染黑
经过修复,没有红色的父节点有红色的子节点了
再把grandparent赋值为新插入的节点,继续套进循环,20这个节点没有uncle,所以为根节点,直接染黑
2.3 如果uncle是黑色的
2.3.1 如果新插入的节点与他的父节点不在一条线上,沿着parent往inline(变成一条线上的)方向转
插入17
沿着parent向右转
parent: 19 newNode:17 ==> newNode:19 parent:17
现在三个节点就都是inline了
2.3.2 现在parent和新插入的节点为inline
newNode:19 parent:17 grandparent:15
沿着grandparent往uncle方向进行一次旋转(uncle为nullptr leaf)
grandparent和parent交换颜色
三、红黑树的删除
代码如下:
*helper method都会放在文章的最下面
// POST: if the parameter exists in tree, deletes it and returns true; otherwise returns false
// PARAM: the value to be removed
template<class T>
bool RedBlackTree<T>::remove(T value)
{
// find target
NodeT<T>* z = findNode(value);
if(z == nullptr){
return false;
}
// if red black tree only has on element, so if the only one element can be found, it is root
if(size() == 1){
root = nullptr;
length--;
return true;
}
NodeT<T>* y;
NodeT<T>* x;
NodeT<T>* xParent;
if(z->left == nullptr || z->right == nullptr){ // z has zero or one children
y = z; //node to be removed
}
else{ // z has two children
y = getPredecessor(z); // y is successor
}
// y only have at most one child, because if y is successor or predecessor, it has zero or one children
// x is y's only one child, or the nullptr
if(y->left != nullptr){
x = y->left;
}
else{
x = y->right;
}
xParent = y->parent;
if(x != nullptr){ // detach x from y
x->parent = y->parent;
}
if (y->parent == nullptr){ // if y is root
root = x;
}
else{
// Attach x to y's parent
if(y == y->parent->left){ // y is left child
y->parent->left = x;
}
else{
y->parent->right = x; // y is right child
}
}
if(y != z){ // if y is the successor
z->data = y->data; // replace z with y
}
if(y->isBlack == true){
rbFix(x, xParent);
}
delete y;
length--;
return true;
}
1. 如果要删掉的节点是红色
非常完美,什么都不用做,直接删了就行,因为删除红色的节点不会影响任何红黑树的属性
2. 如果要删掉的节点是黑色
删掉的节点是黑色的话,会违反红黑树的“所有的路径从根到叶子黑色节点必须一致”这条属性,一条路径删除一个黑色节点,他的黑色节点就比别的路径少一个。
找到要删掉的实际的节点,如果是要删掉的节点有两个子节点的话,把要删掉的节点的successor/predecessor的数据赋值给要删除的节点,然后删除他的predecessor就可以。(删除的其实不是要删的节点,而是要删的节点的predecessor)
2.1 删除节点后接上来的节点是红色,直接染黑就可以
删除25
30接上来,直接染黑
2.2 删除节点接上来的节点是黑色
这时候删掉节点后接上来的节点,我们就给他起名叫做“doubly black”(双黑),双黑只是我们想象的一个东西,我们要把双黑进行修复,把多余的黑色传到别的地方去。
相比于插入的时候我们看的是uncle节点,删除的时候我们要看他的sibling(兄弟姐妹)
2.2.1 如果sibling是红色
删除30
sibling:14, parent:20
把null leaf看成一个双黑
沿着双黑的parent往双黑的方向转
把双黑的grandparent染黑,sibling染红
2.2.2 如果sibling是黑色
a. 如果sibling有红色的子节点
如果sibling的红色child不是inline的话,沿着sibling的inline方向转,把他转成一个inline的
delete 10
null leaf看成是双黑,双黑的sibling16黑色,有红色的节点,不inline,先转成inline
沿着parent往双黑方向转
之后把双黑的parent的sibling染黑
b. 如果sibling全是黑色的子节点,把双黑传递给parent,把sibling变成红色,一直重复向上传,如果传到root,什么都不用做
删除14
sibling染红,双黑重新赋为双黑的父节点(15)
双黑为根节点,什么都不用做,删除完成
Helper method:
// finds the value in the tree and returns it node, if no such value returns nullptr
template<class T>
NodeT<T>* RedBlackTree<T>::findNode(T value) const
{
NodeT<T> *temp = root;
while(temp != nullptr){
if(value == temp->data){
return temp;
}
if(value > temp->data){
temp = temp->right;
}
else{
temp = temp->left;
}
}
return nullptr;
}
// POST: find the target's predecessor in the tree
// PARAM: target
template<class T>
NodeT<T>* RedBlackTree<T>::getPredecessor(NodeT<T> *nd) const
{
NodeT<T> *successor = nd->left;
while(successor->right != nullptr){
successor = successor->right;
}
return successor;
}
// POST: binary search tree insert algorithm
template<class T>
bool RedBlackTree<T>::BST_insert(NodeT<T>* newNode)
{
NodeT<T> *temp = root;
while(temp != nullptr){ // find parent of new node
if(newNode->data > temp->data){
newNode->parent = temp;
temp = temp->right;
}
else if(newNode->data < temp->data){
newNode->parent = temp;
temp = temp->left;
}
else{
return false;
}
}
if(root == nullptr){
root = newNode;
}
else{ // insert new node
if(newNode->data > newNode->parent->data){
newNode->parent->right = newNode;
}
else{
newNode->parent->left = newNode;
}
}
length++;
return true;
}
// POST: left rotate the tree along x
template<class T>
void RedBlackTree<T>::leftRotate(NodeT<T>* x)
{
NodeT<T>* y = x->right;
x->right = y->left;
if (y->left != nullptr){
y->left->parent = x;
}
y->parent = x->parent;
if (x->parent == nullptr){
this->root = y;
}
else if (x == x->parent->left){
x->parent->left = y;
}
else{
x->parent->right = y;
}
y->left = x;
x->parent = y;
}
// POST: right rotate the three along x
template<class T>
void RedBlackTree<T>::rightRotate(NodeT<T>* x)
{
NodeT<T>* y = x->left;
x->left = y->right;
if (y->right != nullptr){
y->right->parent = x;
}
y->parent = x->parent;
if (x->parent == nullptr){
root = y;
}
else if (x == x->parent->right){
x->parent->right = y;
}
else{
x->parent->left = y;
}
y->right = x;
x->parent = y;
}
// PARAM: newNode - the new node after delete a black node
// newNodeParent - the parent of new node
// isLeft - is the new node a left or right child
// POST: fix the tree after delete a black node
template<class T>
void RedBlackTree<T>::rbFix(NodeT<T>* x, NodeT<T>* xParent)
{
NodeT<T>* sibling;
while(x != root && (x == nullptr || x->isBlack == true)){ // x is not the root and is black child
if(x == xParent->left){ // if x is left child
sibling = xParent->right; // x's sibling is a right child
if(sibling->isBlack == false){ // make sibling inline with x
sibling->isBlack = true;
xParent->isBlack = false;
leftRotate(xParent);
sibling = xParent->right;
}
// if sibling's children are all black
if((sibling->left == nullptr || sibling->left->isBlack == true) && (sibling->right == nullptr || sibling->right->isBlack == true)){
sibling->isBlack = false;
x = xParent;
xParent = xParent->parent;
}
else{ // one sibling child is red
if(sibling->right == nullptr || sibling->right->isBlack == true){ // sibling left child is red; red node is not inline, and make it to inline
sibling->left->isBlack = true;
sibling->isBlack = false;
rightRotate(sibling); // make it to inline
sibling = xParent->right;
}
sibling->isBlack = xParent->isBlack;
xParent->isBlack = true;
sibling->right->isBlack = true;
leftRotate(xParent);
x = root;
}
}
else{ // symmetric case
sibling = xParent->left;
if(sibling->isBlack == false){
sibling->isBlack = true;
xParent->isBlack = false;
rightRotate(xParent);
sibling = xParent->left;
}
if((sibling->left == nullptr || sibling->left->isBlack == true) && (sibling->right == nullptr || sibling->right->isBlack == true)){
sibling->isBlack = false;
x = xParent;
xParent = xParent->parent;
}
else{
if(sibling->left == nullptr || sibling->left->isBlack == true){
sibling->right->isBlack = true;
sibling->isBlack = false;
leftRotate(sibling);
sibling = xParent->parent;
}
sibling->isBlack = xParent->isBlack;
xParent->isBlack = true;
sibling->left->isBlack = true;
rightRotate(xParent);
x = root;
}
}
}
x->isBlack = true;
}