目录
红黑树的概念
红黑树的节点结构定义
红黑树的插入
红黑树的验证
实现红黑树完整代码
红黑树的概念
1. 每个结点不是红色就是黑色2. 根节点是黑色的3. 如果一个节点是红色的,则它的两个孩子结点是黑色的(不存在连续的红色节点)4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点5. 每个叶子结点都是黑色的 ( 此处的叶子结点指的是空结点 )
有了上述限制,最短路径就是只包含黑色节点的路径,而最长路径就是黑红交替,所以所有路径长度范围都在[最短路径,最短路径的2倍]这个区间之间, 保证了红黑树近似平衡
红黑树的节点结构定义
//颜色定义成枚举类型
enum Color
{RED,BLACK
};//节点结构定义
template<class K, class V>
struct RBTreeNode
{//三叉链RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;//键值对pair<K, V> _kv;//颜色变量Color _col;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED){}
};
红黑树的插入
插入新节点我们选择插入红色节点,理由如下:
1.插入黑色节点,一定破坏性质4,所有路径的黑色节点个数都要变化
2.插入红色节点,可能破坏性质3,插入红色节点的父亲是黑色节点,没有破坏性质3, 不需要调整;插入红色节点的父亲是红色节点,破坏了性质3, 需要进行调整
综合考虑,插入红色节点的代价相比插入黑色节点小很多,因此我们选择插入红色节点
插入情况的分类:
上面已经提到,插入红色节点的父亲是黑色节点,没有破坏性质3,因此无需调整,因此下面只讨论插入红色节点的父亲是红色节点的情况
处理方法总体分两类
1.插入节点的叔叔节点存在且为红色 --- 变色即可
字母含义: cur --- 插入节点, p --- cur的父亲节点, g --- cur的爷爷节点, u --- cur的叔叔节点
ps: g一定存在,因为p是红色节点, 不可能为根节点, 而g也一定是黑色节点,因为如果是红色节点,说明在cur插入之前红黑树就已经出问题了!
插入之后cur和p都是红色,因为不能出现连续红色节点,我们选择把p变黑,而p变黑之后,g-p-cur的这条路径多了一个黑色节点,因此我们又把g变红,g变红之后,g-u的这条路径又少了一个黑色节点,由于叔叔节点存在且为红色,因此我们又把u变黑即可
ps:变色完之后,发现g变成了红色,而上图的树可能只是一颗子树,因此g变红之后,接着:
1.如果g是根,把g变黑,因为红黑树要求根节点是黑色
2.如果g不是根,g必然有父亲,继续判断g的父亲节点的颜色,如果g的父节点是黑色,停止处理(因为没有再违反红黑树规则了);如果g的父节点是红色,此时又出现了连续的红节点,需要继续处理, 把cur更新到g的位置,继续循环处理!
2.插入节点的叔叔节不存在 或者 存在且为黑色 --- 旋转 + 变色
这种情况下,单纯的变色已经解决不了问题了!需要 旋转+变色 来处理
具体采用哪种旋转方法,在前面的博客 实现AVL树-CSDN博客 已经讲解过了
注意:无论是哪种旋转+变色方式,发现最终当前子树的根节点都变成了黑色,此时无论cur的父节点是黑色还是红色都不违反红黑树的规则,因此旋转+变色完成之后,直接break跳出循环即可!
右单旋 + 变色 (p是g的左,cur是p的左)
左右双旋 + 变色 (p是g的左,cur是p的右)
左单旋 + 变色 (p是g的右,cur是p的右)
右左双旋 + 变色 (p是g的右,cur是p的左)
红黑树旋转代码(除了不需要调平衡因子,其他代码与AVL树一样):
void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;Node* parentParent = parent->_parent;parent->_parent = subR;if (subRL)subRL->_parent = parent;if (parentParent == nullptr){_root = subR;_root->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}}//右单旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parentParent == nullptr){_root = subL;_root->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}}//左右双旋void RotateLR(Node* parent){RotateL(parent->_left);RotateR(parent);}//右左双旋void RotateRL(Node* parent){RotateR(parent->_right);RotateL(parent);}
红黑树插入代码:
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;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (parent && parent->_col == RED) //父亲不存在或者父亲为黑色就不需要继续调整了!{Node* grandfather = parent->_parent;if (grandfather->_left == parent) //父亲是爷爷的左孩子{Node* uncle = grandfather->_right;//1.插入节点的叔叔存在且为红色 --- 父亲和叔叔变黑,爷爷变红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//调整完之后,更新cur, 判断是否还需要调整cur = grandfather;parent = cur->_parent;}//2.uncle不存在 / uncle为黑色 --- 旋转 + 变色else{if (cur == parent->_left) //单纯左边高{RotateR(grandfather); //右单旋//变色parent->_col = BLACK;grandfather->_col = RED;}else //cur是parent的右边{RotateLR(grandfather); //左右双旋//变色cur->_col = BLACK;grandfather->_col = RED;}break; //旋转+变色完之后, 该子树的根变成了黑色,无需继续调整}}else //父亲是爷爷的右孩子{Node* uncle = grandfather->_left;//1.插入节点的叔叔存在且为红色 --- 父亲和叔叔变黑,爷爷变红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//调整完之后,更新cur, 判断是否还需要调整cur = grandfather;parent = cur->_parent;}//2.uncle不存在 / uncle为黑色 --- 旋转 + 变色else{if (cur == parent->_left) //cur是parent的左边,进行右单旋{RotateRL(grandfather); //右左单旋//变色cur->_col = BLACK;grandfather->_col = RED;}else //cur是parent的右边{RotateL(grandfather); //左单旋//变色grandfather->_col = RED;parent->_col = BLACK;}break; //旋转+变色完之后, 该子树的根变成了黑色,无需继续调整}}}_root->_col = BLACK;return true;
}
红黑树的验证
判断是否是红黑树
bool IsRBTree(){if (_root == nullptr) return true;if (_root->_col == RED){cout << "根节点为红色" << endl;return false;}//以最左路径的黑节点的个数作为参考值Node* cur = _root;int BlackCount = 0;while (cur){if (cur->_col == BLACK)BlackCount++;cur = cur->_left;}//调用子函数判断红黑树int count = 0;return _IsRBTree(_root, count, BlackCount);}
private:bool _IsRBTree(Node* root, int count, int BlackCount){if (root == nullptr){if (count != BlackCount) {cout << "黑色节点的个数不相等" << endl;return false;}return true;}//判断节点的孩子节点颜色不好判断, 转化成判断父亲节点颜色if (root->_col == RED && root->_parent->_col == RED){cout << "存在连续的红节点" << endl;return false;}if (root->_col == BLACK)count++;//递归子问题return _IsRBTree(root->_left, count, BlackCount)&& _IsRBTree(root->_right, count, BlackCount);}
验证红黑树
#include "RBTree.h"#include <vector>
void test1()
{int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };RBTree<int, int> t;for (auto e : a){t.Insert(make_pair(e, e));}t.IsRBTree();cout << t.IsRBTree() << endl;
}void test2()
{const int N = 30;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand());cout << v.back() << endl;}RBTree<int, int> t;for (auto e : v){t.Insert(make_pair(e, e));cout << "Insert:" << e << "->" << t.IsRBTree() << endl;}cout << t.IsRBTree() << endl;
}int main()
{//test1();test2();return 0;
}
实现红黑树完整代码
#pragma once#include <iostream>
using namespace std;//颜色定义成枚举类型
enum Color
{RED,BLACK
};//节点结构定义
template<class K, class V>
struct RBTreeNode
{//三叉链RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;//键值对pair<K, V> _kv;//颜色变量Color _col;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(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){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;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (parent && parent->_col == RED) //父亲不存在或者父亲为黑色就不需要继续调整了!{Node* grandfather = parent->_parent;if (grandfather->_left == parent) //父亲是爷爷的左孩子{Node* uncle = grandfather->_right;//1.插入节点的叔叔存在且为红色 --- 父亲和叔叔变黑,爷爷变红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//调整完之后,更新cur, 判断是否还需要调整cur = grandfather;parent = cur->_parent;}//2.uncle不存在 / uncle为黑色 --- 旋转 + 变色else{if (cur == parent->_left) //单纯左边高{RotateR(grandfather); //右单旋//变色parent->_col = BLACK;grandfather->_col = RED;}else //cur是parent的右边{RotateLR(grandfather); //左右双旋//变色cur->_col = BLACK;grandfather->_col = RED;}break; //旋转+变色完之后, 该子树的根变成了黑色,无需继续调整}}else //父亲是爷爷的右孩子{Node* uncle = grandfather->_left;//1.插入节点的叔叔存在且为红色 --- 父亲和叔叔变黑,爷爷变红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//调整完之后,更新cur, 判断是否还需要调整cur = grandfather;parent = cur->_parent;}//2.uncle不存在 / uncle为黑色 --- 旋转 + 变色else{if (cur == parent->_left) //cur是parent的左边,进行右单旋{RotateRL(grandfather); //右左单旋//变色cur->_col = BLACK;grandfather->_col = RED;}else //cur是parent的右边{RotateL(grandfather); //左单旋//变色grandfather->_col = RED;parent->_col = BLACK;}break; //旋转+变色完之后, 该子树的根变成了黑色,无需继续调整}}}_root->_col = BLACK;return true;}//左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;Node* parentParent = parent->_parent;parent->_parent = subR;if (subRL)subRL->_parent = parent;if (parentParent == nullptr){_root = subR;_root->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}}//右单旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parentParent == nullptr){_root = subL;_root->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}}//左右双旋void RotateLR(Node* parent){RotateL(parent->_left);RotateR(parent);}//右左双旋void RotateRL(Node* parent){RotateR(parent->_right);RotateL(parent);}//判断是否是红黑树bool IsRBTree(){if (_root == nullptr) return true;if (_root->_col == RED){cout << "根节点为红色" << endl;return false;}//以最左路径的黑节点的个数作为参考值Node* cur = _root;int BlackCount = 0;while (cur){if (cur->_col == BLACK)BlackCount++;cur = cur->_left;}//调用子函数判断红黑树int count = 0;return _IsRBTree(_root, count, BlackCount);}
private:bool _IsRBTree(Node* root, int count, int BlackCount){if (root == nullptr){if (count != BlackCount) {cout << "黑色节点的个数不相等" << endl;return false;}return true;}//判断节点的孩子节点颜色不好判断, 转化成判断父亲节点颜色if (root->_col == RED && root->_parent->_col == RED){cout << "存在连续的红节点" << endl;return false;}if (root->_col == BLACK)count++;//递归子问题return _IsRBTree(root->_left, count, BlackCount)&& _IsRBTree(root->_right, count, BlackCount);}private:Node* _root = nullptr;
};