红黑树的定义没必要重复了,现在做个简单实现
Color
节点颜色就分两种,定义个枚举最合适了
enum Color { RED, BLACK };
node
节点信息应该有数据data、颜色color、左子节点指针left、右子节点指针right、父节点指针parent(在需要修改树结构的场景,保存父节点指针能省很多事)
template<typename T>
struct Node
{
T data;
Color color;
Node* left;
Node* right;
Node* parent;
Node(T val):data(val),color(RED),left(nullptr),right(nullptr),parent(nullptr){}
};
class
类应该提供基本的插入和删除函数
template<typename T>
class RedBlackTree
{
private:
Node<T>* root;
void rotateLeft(Node<T>*& x);
void rotateRight(Node<T>*& x);
void insertFix(Node<T>*& x);
void removefix(Node<T>*& x);
public:
RedBlackTree() : root(nullptr) {}
~RedBlackTree();
void insert(const T& val);
void remove(const T& val);
Node<T>* search(const T& val) const;
};
查找
template<typename T>
RedBlackTree<T>::Node* RedBlackTree<T>::search(const T& val)const
{
Node<T>* x = root;
while(x != nullptr)
{
if(val < x->data)
{
x = x->left;
}
else if(val > x->data)
{
x = x->right;
}
else
break;
}
return x;
}
左旋
对A节点做左旋操作,其实是将A节点降为其右子节点的左子节点,如图;中间主要是转移、保存各自父子节点关系
template <typename T>
void RedBlackTree<T>::rotateLeft(Node<T>*& x)
{
//获取A右子
Node<T>* y = x->right;
//把G设置成A的左子
x->right = y->left;
if(y->left != nullptr)//对应的将G父节点指向A
{
y->left->parent = x;
}
//把C的父节点指向A的父节
y->parent = x->parent;
if(x == root)//如果A为根,将C设置成新根
{
root == y;
}
else if(x == x->parent->left)//如果x是其父节点的左子节点,则设置y为其父节点的左子节点
{
x->parent->left = y;
}
else //如果x是其父节点的右子节点,则设置y为其父节点的右子节点
{
x->parent->right = y;
}
// 设置y的右子节点为x,并设置x的父节点为y
y->left = x;
x->parent = y;
}
右旋
对A节点做右旋操作,其实是将A节点降为其左子节点的右子节点,如图操作,中间需要是转移、保存各自父子节点关系
template <typename T>
void RedBlackTree<T>::rotateRight(Node<T>*& x)
{
//获取A左子
Node<T>* y = x->left;
//将A左指向F
x->left = y->right;
if(y->right != nullptr)//将F父指向A
{
y->right->parent = x;
}
//B父指向A父节点
y->parent = x->parent;
if(x == root)//如果A是根,将B设置成新根
{
root = y;
}
else if(x == x->parent->left)//如果A为左子,将其父节点左子指向B
{
x->parent->left = y;
}
else //如果A为右子,将其父节点右子指向B
{
x->parent->right = y;
}
y->right = x;
x->parent = y;
}
插入修复
插入的新节点都默认设置为红色,不增加黑高。但是可能会破坏红黑树的基本特性,因此要对可能出现的破坏做修复;
操作节点的父节点为黑色,不会破坏红黑树特性无需额外处理;
操作节点的父节点为红色,不符合红黑树的条件,需分情况处理:
1、叔叔节点是红色 即父叔都是红色,处理方式为:将操作节点的父叔节点都染黑,祖父染红,并对祖父节点递归处理;
2、叔叔节点是黑色,父节点为左。处理方式为:如果操作节点x是右子,对父节点左旋,新的x(原父)为左子,将父节点染黑,祖父节点染红后右旋(原x节点升祖父);
3、叔叔节点是黑色,父节点为右。处理方式为:如果操作节点x是左子,将操作父节点右旋,把x处理成右子,将父节点染黑,祖父节点染红后左旋(原x节点升祖父);
如果最后处理到根节点,将根染黑
template <typename T>
void RedBlackTree<T>::insertFix(Node<T>*& x)
{
//父节点为红时违反特性,需要修复
while(x->parent != nullptr && x->parent->color == RED)
{
if(x->parent == x->parent->parent->left)//如果操作节点的父节点是左子
{
Node<T>* y = x->parent->parent->right;//叔节点
if(y != nullptr && y->color == RED)//父叔都为红色,则将父叔都染黑,将祖父节点染红(此时祖父这一支高度未变,但是祖父和他父节点可能红色冲突,再将祖父节点代入继续调整)
{
x->parent->color = BLACK;
y->color = BLACK;
x->parent->parent->color = RED;
x = x->parent->parent;
continue;
}
else //父红叔黑
{
if(x == x->parent->right)//如果操作节点x是右子,对父节点左旋(操作后新的x为左子且父红叔黑)
{
x = x->parent;
rotateLeft(x);
}
//如果操作节点x是左子,将父节点染黑,祖父节点染红后右旋(原x节点升祖父)
x->parent->color = BLACK;
x->parent->parent->color = RED;
rotateRight(x->parent->parent);
}
}
else //如果操作节点x的父节点是右子
{
Node<T>* y = x->parent->parent->left;//叔节点
if(y != nullptr && y->color == RED)//父叔都为红色,则将父叔都染黑,将祖父节点染红,继续对祖父节点操作
{
x->parent->color = BLACK;
y->color = BLACK;
x->parent->parent->color = RED;
x = x->parent->parent;
continue;
}
else //父红叔黑
{
if(x == x->parent->left)//如果操作节点x是左子,将操作父节点右旋,把x处理成右子
{
x = x->parent;
rotateRight(x);
}
//如果操作节点x是右子,将父节点染黑,祖父节点染红后左旋(原x升祖父)
x->parent->color = BLACK;
x->parent->parent->color = RED;
rotateLeft(x->parent->parent);
}
}
}
root->color = BLACK;
}
插入函数
template<typename T>
void RedBlackTree<T>::insert(const T& val)
{
if(root == nullptr)//根节点为空,直接插入新根黑色
{
root = new Node<T>(val);
root->color = BLACK;
return;
}
Node<T>* x = root;
Node<T>* y = nullptr;
while(x != nullptr)//找到val该插入的叶子节点,y为x父节点,结束循环是x是空
{
y = x;
if(val < x->data)
{
x = x->left;
}
else if(val > x->data)
{
x = x->right;
}
else
{
return;
}
}
Node<T>* z = new Node<T>(val);
z->parent = y;
if( val < y->data)//新节点插入y下方
{
y->left = z;
}
else if(val > y->data)
{
y->right = z;
}
insertFix(z);//修复
}
删除修复
相对插入的默认红色,删除时情况稍显复杂。等完善后补充