赞
踩
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的,不像AVL树那样严格的平衡。
即每条路径上的黑色节点相等
)满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍(最长路径 <= 最短路径×2
),为什么会这样呢?-- 如下图
如果比较AVL树和红黑树的搜索,AVL树的查找效率更高为:logN;
但是在插入和删除时红黑树的效率更高,它不需要严格的平衡,旋转的次数更少。
插入时,我们是插入黑节点好还是红节点好呢?选择破坏性质3还是性质4呢?
那我们肯定选择调整次数少的方式,因此新增节点的颜色给红色(注意:当插入节点为根节点时,应该是黑色)
//颜色 enum Color { RED = 0, BLACK }; template<class K, class V> struct TreeNode { pair<K, V> _kv; TreeNode<K, V>* _left; TreeNode<K, V>* _right; TreeNode<K, V>* _parent;//前驱节点 Color _col; TreeNode(const pair<K, V>& kv) :_kv(kv) , _left(nullptr) , _right(nullptr) , _parent(nullptr) , _col(RED)//默认新节点为红色的 {} };
template<class K,class V> class RBTree { typedef TreeNode<K, V> Node; public: RBTree() :_root(nullptr) {} RBTree(const RBTree<K, V>& tree) { this->_root = Copy(tree._root); } RBTree<K, V>& operator=(RBTree<K, V> tree) { swap(this->_root, tree._root);//现代写法 return *this; } ~RBTree() { Destroy(_root); } bool insert(const pair<K, V>& kv) { } Node* find(const pair<K, V>& kv) { } void InOrder() { _InOrder(_root); cout << endl; } private: void Destroy(Node* root) { if (root == nullptr) return; Destroy(root->_left); Destroy(root->_right); delete root; } Node* Copy(Node* root) { if (root == nullptr) return nullptr; _root->_left = Copy(root->_left); _root->_right = Copy(root->_right); return _root; } void _InOrder(Node* root) { if (root == nullptr) return; _InOrder(root->_left); cout << root->_kv.first << " "; _InOrder(root->_right); } Node* _root; };
对于查找功能,搜索树都是一样的,非常简单就不赘述了。
Node* find(const pair<K, V>& kv)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < kv.first)
cur = cur->_right;
else if (cur->_kv.first > kv.first)
cur = cur->_left;
else
return cur;
}
return nullptr;
}
在插入时,我们要先按照搜索树的规则找到插入位置,然后将新节点插入;新节点插入后,要检查红黑树的规则有没有被破坏。注意:如果新插入的节点就是根节点,需要遵守规则2,根节点应为黑色
。
bool insert(const pair<K, V>& kv) { if (_root == nullptr) { _root = new Node(kv); //如果插入的节点是头节点,需要遵守规则2(根是黑的) //需要将默认的红色改为黑 _root->_col = BLACK; return true; } Node* cur = _root; Node* parent = cur->_parent; 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); cur->_parent = parent; //父亲连孩子 if (parent->_kv.first < kv.first) parent->_right = cur; else parent->_left = cur; //检查是否违反红黑树的规则 //.... }
检测新节点插入后,红黑树的性质是否造到破坏;因为新节点的默认颜色是红色,因此,
则不需要调整
此时需要对红黑树分情况来讨论:
那违反规则后如何调整呢?又分为一下几种情况:
但是当爷爷节点不是子树而是根时,需要将其变成黑色;若是子树,则保持它的红色,继续向上调整。
叔叔不存在
或者叔叔存在且为黑,需要旋转+变色处理右单旋的情况
新增节点为红色的原因如下:
左右双旋的情况
由于旋转的具体代码我们在AVL树种已经写过,这里就不给出具体步骤了。
private: 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; //将连将 SubL->_parent = parentParent; if (parentParent == nullptr) { _root = SubL; } else { if (parentParent->_left == parent) parentParent->_left = SubL; else parentParent->_right = SubL; } } void RotateL(Node* parent) { Node* SubR = parent->_right; Node* SubRL = SubR->_left; //降级连兵 parent->_right = SubRL; if (SubRL) SubRL->_parent = parent; Node* parentParent = parent->_parent; //升级连将 SubR->_left = parent; parent->_parent = SubR; //将连将 SubR->_parent = parentParent; if (parentParent == nullptr) _root = SubR; else { if (parentParent->_left == parent) parentParent->_left = SubR; else parentParent->_right = SubR; } }
当父亲在爷爷左侧时,大体框架就是这样:
当父亲在爷爷的右侧时,也是类似的,我们仅给出旋转时的图:
新增在父亲的右侧,构成“直”树,采用左单旋
右左双旋
新增在父亲的左侧,构成“弯”树,采用右左双旋
具体的代码实现如下:
//grandfather->_right == parent else { Node* uncle = grandfather->_left; //如果叔叔存在且为红,仅需要变色 if (uncle && uncle->_col == RED) { parent->_col = BLACK; uncle->_col = BLACK; grandfather->_col = RED; //继续调整 cur = grandfather; parent = cur->_parent; } //如果叔叔不存在 或者存在且为黑,需要旋转+变色 else { // g // u p // c if (parent->_right == cur) { RotateL(grandfather); parent->_col = BLACK; grandfather->_col = RED; } // g // u p // c else { RotateR(parent); RotateL(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; }
那红黑树的插入就实现完了,完整代码如下:
bool insert(const pair<K, V>& kv) { if (_root == nullptr) { _root = new Node(kv); //如果插入的节点是头节点,需要遵守规则2(根是黑的) //需要默认的红色改为黑 _root->_col = BLACK; return true; } Node* cur = _root; Node* parent = cur->_parent; 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); cur->_parent = parent; //父亲连孩子 if (parent->_kv.first < kv.first) parent->_right = cur; else parent->_left = cur; //----------------------------------------------------------------------- //检查是否违反红黑树的规则 //如果父亲存在且为红,则违反规则 while (parent && parent->_col == RED) { Node* grandfather = parent->_parent; if (grandfather->_left == parent) { //根据爷爷节点找叔叔 Node* uncle = grandfather->_right; //如果叔叔存在且为红,仅需要变色 if (uncle && uncle->_col == RED) { parent->_col = BLACK; uncle->_col = BLACK; grandfather->_col = RED; //然后继续向上调整 cur = grandfather; parent = cur->_parent; } //如果叔叔不存在 或者存在且为黑,需要旋转+变色 else { //判断单旋(直树)还是双旋(弯树) //单旋 if (parent->_left == cur) { // g // p u // c RotateR(grandfather); parent->_col = BLACK; grandfather->_col = RED; } //双旋 else { // g // p u // c RotateL(parent); RotateR(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break;//旋转后已经符合规则,无需再调整了 } } //grandfather->_right == parent else { Node* uncle = grandfather->_left; //如果叔叔存在且为红,仅需要变色 if (uncle && uncle->_col == RED) { parent->_col = BLACK; uncle->_col = BLACK; grandfather->_col = RED; //继续调整 cur = grandfather; parent = cur->_parent; } //如果叔叔不存在 或者存在且为黑,需要旋转+变色 else { // g // u p // c if (parent->_right == cur) { RotateL(grandfather); parent->_col = BLACK; grandfather->_col = RED; } // g // u p // c else { RotateR(parent); RotateL(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } } //父亲不存在,或者父亲为黑时,无需判断,直接将根变黑 _root->_col = BLACK; return true; }
红黑树的检测分为两步:
在检查每条路径上黑节点的个数时,首先要有一个黑节点个数的标准值,可以是最左侧或者最右侧;同时还要有一个变量或者容器来存储当前路径中表黑节点的个数。
bool IsRBTree() { Node* root = _root; if (root == nullptr)//空树也是 return true; if (root->_col == RED) { cout << "根节点为红色,违反规则" << endl; return false; } //先统计最左侧路径上有多少黑节点作为标准 Node* cur = root; int blackNum = 0; while (cur) { if (cur->_col == BLACK) blackNum++; cur = cur->_left; } //k用来记录路径中黑节点的个数 int k = 0; //在依据标准黑节点的个数,比较其它路径 return _IsRBTree(root, blackNum, k); }
有了黑节点的标准值后,我们才可正式开始比较每条路径黑节点的个数
bool _IsRBTree(Node* root, int blackNum, int k) { //一条路径走完,比较黑节点的个数 if (root == nullptr) { if (blackNum != k) { cout << "路径黑节点个数不相等,违反规则" << endl; return false; } return true; } Node* parent = root->_parent; if (parent && root->_col == RED && parent->_col == RED) { cout << "存在连续的红节点,违反规则" << endl; return false; } //统计黑节点 if (root->_col == BLACK) k++; //统计完根,在依次统计左右子树 return _IsRBTree(root->_left, blackNum, k) && _IsRBTree(root->_right, blackNum, k); }
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。
namespace my { //颜色 enum Color { RED = 0, BLACK }; template<class K, class V> struct TreeNode { pair<K, V> _kv; TreeNode<K, V>* _left; TreeNode<K, V>* _right; TreeNode<K, V>* _parent;//前驱节点 Color _col; TreeNode(const pair<K, V>& kv) :_kv(kv) , _left(nullptr) , _right(nullptr) , _parent(nullptr) , _col(RED)//默认新节点为红色的 {} }; template<class K,class V> class RBTree { typedef TreeNode<K, V> Node; public: RBTree() :_root(nullptr) {} RBTree(const RBTree<K, V>& tree) { this->_root = Copy(tree._root); } RBTree<K, V>& operator=(RBTree<K, V> tree) { swap(this->_root, tree._root);//现代写法 return *this; } ~RBTree() { Destroy(_root); } bool insert(const pair<K, V>& kv) { if (_root == nullptr) { _root = new Node(kv); //如果插入的节点是头节点,需要遵守规则2(根是黑的) //需要默认的红色改为黑 _root->_col = BLACK; return true; } Node* cur = _root; Node* parent = cur->_parent; 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); cur->_parent = parent; //父亲连孩子 if (parent->_kv.first < kv.first) parent->_right = cur; else parent->_left = cur; //检查是否违反红黑树的规则 //如果父亲存在且为红,则违反规则 while (parent && parent->_col == RED) { Node* grandfather = parent->_parent; if (grandfather->_left == parent) { //根据爷爷节点找叔叔 Node* uncle = grandfather->_right; //如果叔叔存在且为红,仅需要变色 if (uncle && uncle->_col == RED) { parent->_col = BLACK; uncle->_col = BLACK; grandfather->_col = RED; //然后继续向上调整 cur = grandfather; parent = cur->_parent; } //如果叔叔不存在 或者存在且为黑,需要旋转+变色 else { //判断单旋(直树)还是双旋(弯树) //单旋 if (parent->_left == cur) { // g // p u // c RotateR(grandfather); parent->_col = BLACK; grandfather->_col = RED; } //双旋 else { // g // p u // c RotateL(parent); RotateR(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break;//旋转后已经符合规则,无需再调整了 } } //grandfather->_right == parent else { Node* uncle = grandfather->_left; //如果叔叔存在且为红,仅需要变色 if (uncle && uncle->_col == RED) { parent->_col = BLACK; uncle->_col = BLACK; grandfather->_col = RED; //继续调整 cur = grandfather; parent = cur->_parent; } //如果叔叔不存在 或者存在且为黑,需要旋转+变色 else { // g // u p // c if (parent->_right == cur) { RotateL(grandfather); parent->_col = BLACK; grandfather->_col = RED; } // g // u p // c else { RotateR(parent); RotateL(grandfather); cur->_col = BLACK; grandfather->_col = RED; } break; } } } //父亲不存在,或者父亲为黑时,无需判断,直接将根变黑 _root->_col = BLACK; return true; } Node* find(const pair<K, V>& kv) { Node* cur = _root; while (cur) { if (cur->_kv.first < kv.first) cur = cur->_right; else if (cur->_kv.first > kv.first) cur = cur->_left; else return cur; } return nullptr; } void InOrder() { _InOrder(_root); cout << endl; } bool IsRBTree() { Node* root = _root; if (root == nullptr)//空树也是 return true; if (root->_col == RED) { cout << "根节点为红色,违反规则" << endl; return false; } //先统计最左侧路径上有多少黑节点作为标准 Node* cur = root; int blackNum = 0; while (cur) { if (cur->_col == BLACK) blackNum++; cur = cur->_left; } //k用来记录路径中黑节点的个数 int k = 0; //在依据标准黑节点的个数,比较其它路径 return _IsRBTree(root, blackNum, k); } int Size() { return _Size(_root); } int Height() { return _Height(_root); } private: int _Height(Node* root) { if (root == nullptr) return 0; int leftH = _Height(root->_left); int rightH = _Height(root->_right); return leftH > rightH ? leftH + 1 : rightH + 1; } int _Size(Node* root) { if (root == nullptr) return 0; return 1 + _Size(root->_left) + _Size(root->_right); } bool _IsRBTree(Node* root, int blackNum, int k) { //一条路径走完,比较黑节点的个数 if (root == nullptr) { if (blackNum != k) { cout << "路径黑节点个数不相等,违反规则" << endl; return false; } return true; } Node* parent = root->_parent; if (parent && root->_col == RED && parent->_col == RED) { cout << "存在连续的红节点,违反规则" << endl; return false; } //统计黑节点 if (root->_col == BLACK) k++; //统计完根,在依次统计左右子树 return _IsRBTree(root->_left, blackNum, k) && _IsRBTree(root->_right, blackNum, k); } 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; //将连将 SubL->_parent = parentParent; if (parentParent == nullptr) { _root = SubL; } else { if (parentParent->_left == parent) parentParent->_left = SubL; else parentParent->_right = SubL; } } void RotateL(Node* parent) { Node* SubR = parent->_right; Node* SubRL = SubR->_left; //降级连兵 parent->_right = SubRL; if (SubRL) SubRL->_parent = parent; Node* parentParent = parent->_parent; //升级连将 SubR->_left = parent; parent->_parent = SubR; //将连将 SubR->_parent = parentParent; if (parentParent == nullptr) _root = SubR; else { if (parentParent->_left == parent) parentParent->_left = SubR; else parentParent->_right = SubR; } } void Destroy(Node* root) { if (root == nullptr) return; Destroy(root->_left); Destroy(root->_right); delete root; } Node* Copy(Node* root) { if (root == nullptr) return nullptr; _root->_left = Copy(root->_left); _root->_right = Copy(root->_right); return _root; } void _InOrder(Node* root) { if (root == nullptr) return; _InOrder(root->_left); cout << root->_kv.first << " "; _InOrder(root->_right); } Node* _root; }; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。