红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是
Red或
Black。通过对任何一条从根到叶子节点简单路径上的
颜色来约束树的高度,红黑树保证
最长路径不超过最短路径的两倍,因而近似于平衡。
红黑树是满足下面红黑性质的二叉搜索树:1. 每个节点,不是红色就是黑色的 2. 根节点是黑色的 3. 如果一个节点是红色的,则它的两个子节点是黑色的(不存在连续的红色节点)4. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。 思考:为什么满足上面的颜色约束性质,红黑树能保证最长路径不超过最短路径的两倍? 最短的路径上节点的颜色全部都为黑色;最长的路径则为黑红交叉的路径,其上有与最短路径的黑节点数目相同的黑节点数和红节点数目。所以我们按照红黑树性质所建立的红黑树的最长路径必然不会超过最短路径的两倍!
建立红黑树的节点类:插入的新节点默认是红色的。原因是:插入黑节点必然会影响
所有路径都含有相同数目的黑色节点这一原则,较难维护!?
| 12345678910111213141516171819202122232425 | enum Color{ RED, BLACK}; template<class K,class V>struct RBTreeNode{ K _key; V _value; Color _color; RBTreeNode<K, V>* _left; RBTreeNode<K, V>* _right; RBTreeNode<K, V>* _parent; RBTreeNode(const K& key=K(), const V&value=V()) :_key(key) , _value(value) , _color(RED) , _left(NULL) , _right(NULL) , _parent(NULL) {}}; |
红黑树需要变色或利用旋转来降低高度的几种情况:图注:g代表grandfather祖父节点;p代表parent父亲结点;u代表uncle叔叔节点;cur代表当前节点
一、父节点是祖父节点的左孩子1.uncle的颜色是红色①当前节点cur是parent的左孩子
②当前节点cur是parent的右孩子
2.uncle的颜色是黑色 或者 uncle为NULL①cur是parent的左孩子,右单旋
②cur是parent的右孩子,先左后右双旋
二、父节点是祖父节点的右孩子1.uncle的颜色是红色①cur是parent的右孩子
②cur是parent的左孩子
2.uncle的颜色是黑色 或者 uncle为NULL①cur是parent的右孩子
②cur是parent的左孩子插入节点:
- 首先,要找到插入该节点的位置,找到后就插入节点
- 然后,对红黑树的节点颜色的合法性进行检查,并根据检查结果进行变色或者旋转。
基于以上的情况,红黑树利用模板类封装的插入函数算法就完成了:
| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 | template<class K, class V>bool RBTree<K, V>::Insert(const K& key, const V& value){ if (_root == NULL) { _root = new RBTreeNode<K, V>(key, value); _root->_color = BLACK; return true; } RBTreeNode<K, V>* cur = _root; RBTreeNode<K, V>* parent = NULL; 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 RBTreeNode<K, V>(key, value); cur->_parent = parent; if (parent->_key > key) parent->_left = cur; else if (parent->_key < key) parent->_right = cur; while (parent&&parent->_color==RED) { RBTreeNode<K, V>* grandfather = parent->_parent; if (parent == grandfather->_left) { RBTreeNode<K, V>* uncle = grandfather->_right; if (uncle&&uncle->_color == RED) { grandfather->_color = RED; parent->_color =BLACK; uncle->_color = BLACK; cur = grandfather; parent = grandfather->_parent; } else if( (uncle&&uncle->_color==BLACK)||uncle==NULL) { if (cur == parent->_left) { parent->_color = BLACK; grandfather->_color = RED; RotateR(grandfather); } else { RotateL(parent); parent->_color = BLACK; grandfather->_color = RED; RotateR(grandfather); } break; } } else if (parent == grandfather->_right) { RBTreeNode<K, V>* uncle = grandfather->_left; if (uncle&&uncle->_color == RED) { grandfather->_color = RED; parent->_color = BLACK; uncle->_color = BLACK; cur = grandfather; parent = cur->_parent; } else if( (uncle&&uncle->_color == BLACK)||uncle==NULL) { if (cur == parent->_right) { parent->_color = BLACK; grandfather->_color = RED; RotateL(grandfather); } else if (cur==parent->_left) { RotateR(parent); parent->_color = BLACK; grandfather->_color = RED; RotateL(grandfather); } break; } } } _root->_color = BLACK; return true;} |
插入完成之后,我们无法直观的看出红黑树的节点颜色是否合法,也无法直观的看出每条路径的黑色节点数目是否相同。所以,这里实现两个函数,方便检验红黑树的合法性。
- 红黑树每条路径的黑色节点数目都相同,所以随意遍历一条路径,计算这条路上的黑色节点的数目。以该数据为标杆,和其他路径的黑色节点数目作比较,判断是否都相同。
- 如果当前节点是红颜色并且它有父节点,那么再判断父节点的颜色是否也是红色,这样就能判断该树是否满足连续两个节点不能同时为红色这一性质。
| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 | template<class K, class V>bool RBTree<K, V>::Check(){ int blackNum = 0; RBTreeNode<K, V>* cur = _root; while (cur) { if (cur->_color == BLACK) blackNum++; cur = cur->_left; } int CBNum = 0; return _Check(_root,blackNum,CBNum);} template<class K, class V>bool RBTree<K, V>::_Check(RBTreeNode<K, V>* root, int blackNum, int CBNum){ if (root == NULL) return true; if (root->_color == BLACK) { CBNum++; if (root->_left == NULL&&root->_right == NULL) { if (blackNum == CBNum) { return true; } else { cout << "叶子节点为" << root->_key << "路径的黑色节点数目与最左侧支路的黑色节点数目不相等 !" << endl; return false; } } } else if (root->_parent&&root->_parent->_color == RED) { cout << root->_parent->_key << " 和 " << root->_key << " 为两个连续的红色节点" << endl; return false; } return _Check(root->_left, blackNum, CBNum) && _Check(root->_right, blackNum, CBNum);} |
红黑树和AVL树的比较 红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都是O(lg(N)) 红黑树的不追求完全平衡,保证最长路径不超过最短路径的2倍,相对而言,降低了旋转的要求,所以性能会优于AVL树,所以实际运用 中红黑树更多。
本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-07/133275.htm