【数据结构】红黑树(如何实现及怎样判断)
2016-06-16 16:02
330 查看
红黑树是一颗二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是red或black。通过对任何一条从根节点到叶子节点的简单路径上的颜色来约束,红黑树保证了最长路径不超过最短路经的两倍,因此近似于平衡。
红黑树的规则:
1、每个节点不是红色就是黑色的。
2、根结点是黑色的。
3、如果一个节点是红色的,则它的两个子结点是黑色的。即每条路径上不能存在两个连续的红节点。
4、对每个节点,从该节点到其他节点的简单路径上,均包含相同数目的黑色节点。
红黑树节点RBTreeNode的实现,利用三叉链(left、right、parent)、key、value及颜色col。
一、红黑树的插入
红黑树的插入类似于二叉搜索树,但是每次插入后都到要注意是否满足红黑树的规则,特别是规则3和规则4。如果不满足就需要调整树的结构,下面对插入节点时分成以下几种情况:
注:cur(插入节点)parent(cur的父亲节点)grandfather(parent的父亲节点)uncle(父亲的兄弟节点)
1、根节点root为空,直接插入新节点并给root,设置根节点的颜色为BLACK。
2、根节点root不为空,找到插入节点的位置并插入节点cur。cur节点是红色,若parent是红节点,则需要进行调整。
存在以下三种情况:
情况一:cur为红,parent为红,grandfather为黑,uncle存在且为红。
调整方案,如下如所示:
情况二:cur为红,parent为红,grandfather为黑,uncle不存在或者uncle为黑。
左单旋:parent为grantfather的右孩子,cur为parent的右孩子。
右单旋: parent为grantfather的左孩子,cur为parent的左孩子。
调整方案,下面以右单旋进行分析,如下图所示:
左单旋转类似右单旋转的实现过程。
情况三:cur为红,parent为红,grantfather为黑,uncle不存在或者uncle为黑。
左右单旋:parent为grantfather的右孩子,cur为parent的左孩子。
右左单旋:parent为grantfather的左孩子,cur为parent的右孩子。
调整方案,下面以做右单旋进行分析,如下图所示:
右左旋转的实现类似左单旋转的实现过程。
具体实现如下:
二、红黑树的判断
1、根结点是否满足红黑树规则,是否为黑色。
2、每条路径的黑色节点相等。统计出一条路径的黑色节点的个数,然后与其他路径黑色节点个数进行比较。
3、不存在连续的红色节点,判断红色节点的父亲节点是否为红色。
具体实现如下:
红黑树的规则:
1、每个节点不是红色就是黑色的。
2、根结点是黑色的。
3、如果一个节点是红色的,则它的两个子结点是黑色的。即每条路径上不能存在两个连续的红节点。
4、对每个节点,从该节点到其他节点的简单路径上,均包含相同数目的黑色节点。
红黑树节点RBTreeNode的实现,利用三叉链(left、right、parent)、key、value及颜色col。
enum colour { RED, BLACK, }; template<class K, class V> struct RBTreeNode { RBTreeNode<K, V>* _left; RBTreeNode<K, V>* _right; RBTreeNode<K, V>* _parent; K _key; V _value; colour _col; RBTreeNode(const K& key = K(), const V& value = V()) :_left(NULL) , _right(NULL) , _parent(NULL) , _key(key) , _value(value) , _col(RED)//初始化插入节点的颜色为红色,不影响黑节点个数 {} };
一、红黑树的插入
红黑树的插入类似于二叉搜索树,但是每次插入后都到要注意是否满足红黑树的规则,特别是规则3和规则4。如果不满足就需要调整树的结构,下面对插入节点时分成以下几种情况:
注:cur(插入节点)parent(cur的父亲节点)grandfather(parent的父亲节点)uncle(父亲的兄弟节点)
1、根节点root为空,直接插入新节点并给root,设置根节点的颜色为BLACK。
2、根节点root不为空,找到插入节点的位置并插入节点cur。cur节点是红色,若parent是红节点,则需要进行调整。
存在以下三种情况:
情况一:cur为红,parent为红,grandfather为黑,uncle存在且为红。
调整方案,如下如所示:
情况二:cur为红,parent为红,grandfather为黑,uncle不存在或者uncle为黑。
左单旋:parent为grantfather的右孩子,cur为parent的右孩子。
右单旋: parent为grantfather的左孩子,cur为parent的左孩子。
调整方案,下面以右单旋进行分析,如下图所示:
左单旋转类似右单旋转的实现过程。
情况三:cur为红,parent为红,grantfather为黑,uncle不存在或者uncle为黑。
左右单旋:parent为grantfather的右孩子,cur为parent的左孩子。
右左单旋:parent为grantfather的左孩子,cur为parent的右孩子。
调整方案,下面以做右单旋进行分析,如下图所示:
右左旋转的实现类似左单旋转的实现过程。
具体实现如下:
RBTree() :_root(NULL) {} bool Insert(const K& key, const V& value) { //1、_root为空时,插入节点是根节点 if (_root == NULL) { _root = new Node(key, value); _root->_col = BLACK; return true; }
Node* parent = NULL; Node* cur = _root; while (cur)//找到插入节点的位置 { if (cur->_key > key) { parent = cur; cur = cur->_left; } else if (cur->_key < key) { parent = cur; cur = cur->_right; } else return false; } cur = new Node(key, value);//插入节点 if (parent->_key > cur->_key) { parent->_left = cur; cur->_parent = parent; } if (parent->_key < cur->_key) { parent->_right = cur; cur->_parent = parent; } //2、出现两个连续红节点,进行调整 while (cur != _root && parent->_col == RED)//此条件说明存在parent节点,parent存在父亲节点 { Node* grandfather = parent->_parent; if (parent == grandfather->_left) { Node* uncle = grandfather->_right; if (uncle && uncle->_col == RED)//情况一:uncle为RED { grandfather->_col = RED; parent->_col = BLACK; uncle->_col = BLACK; } else //情况二、三:uncle为BLACK或不存在(右单旋或左右单旋) {//先考虑左右单旋,先进行左单旋,转化为情况二,再进行右单旋 if (cur == parent->_right) { _RotateL(parent);//左旋不需要变颜色 } _RotateR(grandfather); } } else { Node* grandfather = parent->_parent; if (parent == grandfather->_right) { Node* uncle = grandfather->_left; if (uncle && uncle->_col == RED)//情况一:uncle为RED { grandfather->_col = RED; parent->_col = BLACK; uncle->_col = BLACK; } else //情况二、三:uncle为BLACK或不存在(左单旋或右左单旋) { if (cur == parent->_left) { _RotateR(parent);//右旋不需要变颜色 } _RotateL(grandfather); } } } cur = grandfather; parent = cur->_parent; _root->_col = BLACK; } } void _RotateL(Node* parent) { Node* SubR = parent->_right; Node* SubRL = SubR->_left;
parent->_right = SubRL; if (SubRL) SubRL->_parent = parent; SubR->_parent = parent->_parent; SubR->_left = parent; parent->_parent = SubR; //变色 SubR->_col = BLACK;//情况二中:parent变黑,grandfather变红 parent->_col = RED;
parent = SubR; if (parent->_parent == NULL) _root = parent; else { Node* ppNode = parent->_parent; if (ppNode->_key > parent->_key) ppNode->_left = parent; else ppNode->_right = parent; } } void _RotateR(Node* parent) { Node* SubL = parent->_left; Node* SubLR = SubL->_right;
parent->_left = SubLR; if (SubLR) SubLR->_parent = parent; SubL->_parent = parent->_parent; SubL->_right = parent; parent->_parent = SubL; //变色 SubL->_col = BLACK;//情况二中:parent变黑,grandfather变红 parent->_col = RED;
parent = SubL; if (parent->_parent == NULL) _root = parent; else { Node* ppNode = parent->_parent; if (ppNode->_key > parent->_key) ppNode->_left = parent; else ppNode->_right = parent; } }
二、红黑树的判断
1、根结点是否满足红黑树规则,是否为黑色。
2、每条路径的黑色节点相等。统计出一条路径的黑色节点的个数,然后与其他路径黑色节点个数进行比较。
3、不存在连续的红色节点,判断红色节点的父亲节点是否为红色。
具体实现如下:
bool Check() { if (_root->_col == RED) return false; int count = 0;//统计出一条路径的黑色节点的个数 int num = 0;//需要与count比较的其他路径黑色节点个数 Node* cur = _root; while (cur) { if (cur->_col == BLACK) count++; cur = cur->_left; } return _Check(_root, count, num); }
bool _Check(Node* root, int BlackNum, int CurBlackNum) { if (root == NULL) return true; if (root->_col == RED && root->_parent->_col == RED)//存在两个连续的红节点 return false; if (root->_col == BLACK)//黑节点就CurBlackNum++ CurBlackNum++; if (root->_left == NULL && root->_right == NULL) { if (CurBlackNum == BlackNum) return true; else//黑色节点不相等返回false return false; } return _Check(root->_left, BlackNum, CurBlackNum) && _Check(root->_right, BlackNum, CurBlackNum);//进行左右递归 }以上为个人学习的一些总结,如有纰漏,请多多指教。
相关文章推荐
- 自己组建Model结构
- 数据结构——线性表
- 数据结构与算法 - 3.4 队列ADT
- Redis数据结构详解,五种数据结构分分钟掌握
- Redis数据结构详解,五种数据结构分分钟掌握
- 程序员面试50题(2)—二元查找树的后序遍历结果[数据结构]
- 数据结构之快排
- 大话数据结构——查找
- 数据结构问题汇总
- pandas数据结构基础
- 【数据结构】红黑树的插入(Insert)
- 数据结构--‘搜索二叉树’
- 数据结构_P16
- 前端数据结构与算法系列(转)
- Link-Cut Tree学习总结
- 数据结构-01-字符串(String)
- leetcode——Search Insert Position
- leetcode——Search for a Range
- 常见数据结构
- 【排序】直接选择排序