目录
一、概念和规则:
1、思考为什么最长路径不超过最短路径的二倍?
2、红黑树的效率?
二、红黑树的代码实现
1、红黑树的节点结构
2、红黑树的插入
1、大致过程:
2、维护的三种情况:
1、情况一:变色
2、情况二:单旋+变色
3、情况三:双旋+变色
3、红黑树的验证
4、整体代码
一、概念和规则:
- 红黑树是一颗搜索二叉树,满足左小右大(或者左小右大)的规则;(前提规则)
- 红黑树每一个节点不是黑色就是红色;(规则一)
- 根节点是黑色的;(规则二)
- 不能出现连续的红色节点,若一个节点是红色的,那么它的两个孩子都是黑色的;(规则三)
- 对于任何一条简单路径(从根到空),其上的黑色节点的数量都是相同的;(规则四)
- 红黑树的最长路径不超过最短路径的二倍(规则推论)
1、思考为什么最长路径不超过最短路径的二倍?
由于每条路径的黑色节点的数量的个数相同,极端情况下最短路径的长度就是全是黑色节点的数量,最长路径长度就是红色黑色相间的路径,那么恰好就是最短路径的二倍;那么其他的路径长度都在最长与最短之间,那么最长路径就不会超过最短路径的二倍。
2、红黑树的效率?
假设红黑树最短路径的高度为H,那么最长路径的长度最长为 2*H ,其他的都在这两者之间,那么节点的数量N(根据等比求和)就在 2^H-1到2^(2*H)-1之间,那么红黑树增删查的时间复杂度还是O(logN),和AVL属于同一量级;
AVL是通过左右子树高度差来控制平衡;而红黑树是通过规则和颜色来达到近似平衡。
二、红黑树的代码实现
1、红黑树的节点结构
enum Color
{RED,BLACK
};template<class K,class V>
struct RBTreeNode
{Color _col;pair<K, V> _kv;RBTreeNode<K, V>* _parent;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode(const pair<K,V> kv):_kv(kv),_parent(nullptr),_left(nullptr),_right(nullptr){}
};
这里用枚举定义颜色,方便观察;
2、红黑树的插入
1、大致过程:
- 先按照二叉搜索树进行插入;
- 若是空树,就将新插入的节点认作根节点,并且把颜色赋为黑色;若不是空树,那么插入之后,这个新插入的节点必须赋为红色,否则会破坏所有路径黑色节点数量相同的规则;但是连续的两个红色也会破坏,但是连续的两个红色更好维护;
- 插入红色的,若插入的父节点是黑色的,那么就是正常的,无需维护,插入完成直接退出;
- 插入红色的,若插入的父节点是红色的,那就需要维护,因为此时破坏了规则;而维护有三种情况,维护的关键是看叔节点,也就是父亲的兄弟节点;
2、维护的三种情况:
1、情况一:变色
此种情况出现适用于:当uncle节点存在并且是红色时;
首先grandfather节点(parent和uncle的父节点)时黑色的,parent和uncle节点是红色的,这是确定的条件;此时给parent插入红色的新节点,要维护规则;进行变色:将grandfather变红,parent和uncle变黑;
一次变完之后,因为grandfather变红了,但是其上可能还有节点,当其父节点为红时,还需要继续调整,将cur赋值为grandfather,parent跟着向上调,uncle随之变化;直到父节点的颜色为黑或者父节点为根节点时结束循环;最后会单独处理根节点,无论根节是哪种颜色,都赋为黑;
2、情况二:单旋+变色
适用情形,uncle为空或者为黑时;此时只是单纯的变色解决不了问题,因为每条简单路径上面的黑色节点数量会不同;
当uncle不存在时,那么cur一定是新插入进来的,若不是新插入的那么cur下面还有黑色节点,那么parent的左右子树的黑色节点数量就不同;若uncle存在且为黑色,那么cur一定不是新插入的红色节点,而是cur下面的节点通过变色变上来的红色节点,否则parent的左右子树黑色节点的数量就不同,违反规则;
那么就要将grandfather作为旋转基准进行左单旋或者右单旋,旋转之后parent成了cur和grandfather的根节点,此时再把parent变黑,grandfather变红 ;无论是uncle存在与否,最后都能达到维护的目的,并且每条简单路径上面的黑色节点的数量都相同。
旋转并且变色之后这颗红黑树整体上就已经满足规则了,无需像情况一一样向上调整。
3、情况三:双旋+变色
首先和情况二前提相同:若uncle不存在,则cur一定为新插入的;若uncle存在且为黑,则cur一定不是新插入的,而是调整上来的;
双旋的原因:不同于单旋,当parent是grandfather的左节点但是cur是father的右节点时,单旋不能解决问题,若是单旋,只是交换了左右位置,本质上还是没有完成规则的维护;此时需要双旋之后再变色;
第一次循环以parent为基准旋转,第二次以grandfather为基准旋转;最后将cur变为黑,grandfather变为红;
3、红黑树的验证
红黑树的验证要严格按照四条规则来,验证每一条规则是否被满足,只有当所有的规则都被满足才说明这棵树是红黑树;
- 直接判断根节点是否是黑色
- 每个节点不是黑就是红,这个天然而成无需判断
- 检查是否有红色连续的情况,通过递归每次检查当前节点的父亲,当前节点为红色并且当前节点的父节点也是红色时,就返回false;检查父节点更方便;若是每次检查当前节点的左右子树节点相较麻烦一点
- 检查每条简单路径上面是否含有相同黑色节点数量时,先计算一条路径上面的黑色节点数量作为一个比较的基准值,再在递归的过程中计算每条路径的黑色节点数量,当递归的根为空时说明一条路径走完了,此时比较,若不想等就返回false;整体的返回是左右子树通过&&连接来返回,也就是有一条不满足的这棵树就不是一颗红黑树的意思
4、整体代码
#include<iostream>
using namespace std;enum Color
{RED,BLACK
};template<class K,class V>
struct RBTreeNode
{Color _col;pair<K, V> _kv;RBTreeNode<K, V>* _parent;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode(const pair<K,V> kv):_kv(kv),_parent(nullptr),_left(nullptr),_right(nullptr){}
};template<class K,class V>
class RBTree
{using Node = RBTreeNode<K, V>;
public:void Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;}Node* parent = nullptr;Node* cur = _root;while (cur){if (kv.first < cur->_kv.first){parent = cur;cur = cur->_left;}else if (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}else{return;}}cur = new Node(kv);if (kv.first < parent->_kv.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;cur->_col = RED;//parent为空或者 parent颜色为黑色,插入了满足条件,不用进循环//进循环说明 parent为红并且其父节点为黑,新插入的只能是红while (parent && parent->_col == RED){Node* grandfather = parent->_parent;//两个大方向:parent在 grandfather左边或者右边if (grandfather->_left == parent){Node* uncle = grandfather->_right;//首先是不用旋转的情况,此时无论 cur在 parent哪边都一样//将 grandfather变红,father和 uncle变黑//这种情况就是 uncle存在并且为红时if (uncle && uncle->_col == RED){grandfather->_col = RED;parent->_col = uncle->_col = BLACK;cur = grandfather;parent = cur->_parent;}//这里的 else含义是:uncle为空或者不为空但是颜色是黑色//要旋转+变色else{//uncle若是为空,cur就必是新插入的节点,否则每一条路径的黑色节点数量不同//uncle若不为空,cur就必不是新插入的节点,这样每条路径的黑色节点数量才可能相同//无论为空为否,都要进行旋转再变色if (parent->_left == cur){//右单旋RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;break;//旋转完之后满足规则}else{//先左旋再右旋RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;break;}}}//parent为 grandfather的右孩子else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){grandfather->_col = RED;parent->_col = uncle->_col = BLACK;cur = grandfather;parent = cur->_parent;}else{if (parent->_right == cur){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;break;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;break;}}}}//将根节点置为黑色_root->_col = BLACK;}Node* Find(const K& k){Node* cur = _root;while (cur){if (k < cur->_kv.first){cur = cur->_left;}else if (k > cur->_kv.first){cur = cur->_right;}else{return cur;}}}bool IsRBTree(){if (!_root)return true;if (_root->_col == RED)return false;int mode_count = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)mode_count++;cur = cur->_left;}return Check(_root, 0, mode_count);}void Print(){_Print(_root);cout << endl;}private:Node* _root = nullptr;//右单旋void RotateR(Node* sub){Node* subl = sub->_left;Node* sublr = subl->_right;sub->_left = sublr;if (sublr)sublr->_parent = sub;Node* sub_P = sub->_parent;subl->_right = sub;sub->_parent = subl;if (!sub_P){_root = subl;subl->_parent = nullptr;}else{if (sub_P->_left == sub){sub_P->_left = subl;}else{sub_P->_right = subl;}subl->_parent = sub_P;}}//左单旋void RotateL(Node* sub){Node* subr = sub->_right;Node* subrl = subr->_left;sub->_right = subrl;if (subrl){subrl->_parent = sub;}Node* sub_P = sub->_parent;subr->_left = sub;sub->_parent = subr;if (!sub_P){_root = subr;subr->_parent = nullptr;}else{if (sub_P->_left == sub){sub_P->_left = subr;}else{sub_P->_right = subr;}subr->_parent = sub_P;}}void _Print(Node* root){if (!root){return;}_Print(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_Print(root->_right);}bool Check(Node* root, int count, const int mode_count){if (root == nullptr){if (count != mode_count){cout << "存在不同路径的黑色节点数量不同";return false;}return true;}if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << "存在连续的红色节点";return false;}if (root->_col == BLACK)count++;return Check(root->_left, count, mode_count) && Check(root->_right, count, mode_count);}
};