赞
踩
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
enum Colour { RED, BLACK }; template<class K, class V> struct RBTreeNode { public: pair<K, V> _kv; Colour _col; RBTreeNode<K, V>* _pLeft; RBTreeNode<K, V>* _pRight; RBTreeNode<K, V>* _pParent; RBTreeNode(const pair<K, V>& kv) // 构造新增节点 :_pLeft(nullptr) , _pRight(nullptr) , _pParent(nullptr) , _kv(kv) , _col(RED) // 默认为红色 {} };
红黑树是一种自平衡的二叉搜索树,其节点上会附加一个颜色属性,可以是红色或黑色。在红黑树中将节点默认颜色设置为红色有助于满足红黑树的性质,确保树的平衡性和高效性。
具体原因如下:
- 约束节点插入位置:当新节点插入红黑树时,将其默认设为红色可以帮助维持红黑树的平衡性,因为红色节点的存在不会破坏红黑树的性质,而黑色节点的插入可能会导致需要进行额外的旋转操作来调整平衡。
- 简化旋转操作:默认将节点设置为红色可以减少旋转操作的次数,因为插入红色节点后,可以通过变换颜色和旋转来达到平衡,而插入黑色节点则可能需要更多的旋转操作。
- 保证黑色平衡:红黑树要求任意一条路径上的黑色节点数量相等,因此默认将节点设置为红色可以更容易地保证这一性质,避免破坏黑色平衡。
总的来说,将节点默认设置为红色是为了简化插入和维护红黑树的操作,同时确保树的平衡性和高效性。
为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent
域指向红黑树的根节点,pLeft
域指向红黑树中最小的节点,_pRight
域指向红黑树中最大的节点,如下:
在代码实现中,红黑树的插入操作主要包括以下步骤:
// 在RB树中插入值为kv的节点 bool Insert(const pair<K, V>& kv) { // 插入节点 if (_pRoot == nullptr) { _pRoot = new Node(kv); _pRoot->_col = BLACK; return true; } Node* pParent = nullptr; Node* pCur = _pRoot; while (pCur) { pParent = pCur; if (kv.first < pCur->_kv.first) pCur = pCur->_pLeft; // 往左子树查找 else if (kv.first > pCur->_kv.first) pCur = pCur->_pRight; // 往右子树查找 else return false; // 重复值不插入 } // 创建新节点 pCur = new Node(kv); // 红色的 if (kv.first < pParent->_kv.first) pParent->_pLeft = pCur; else pParent->_pRight = pCur; pCur->_pParent = pParent; // 插入节点后,更新 while (pParent && pParent->_col == RED) { Node* pGrandfather = pParent->_pParent; if (pGrandfather == nullptr) break; // 处理根节点 // 找叔叔 if (pParent == pGrandfather->_pLeft) { Node* pUncle = pGrandfather->_pRight; // case1: 叔叔存在且为RED if (pUncle && pUncle->_col == RED) { // 变色 pParent->_col = pUncle->_col = BLACK; pGrandfather->_col = RED; // 继续往上处理 pCur = pGrandfather; pParent = pCur->_pParent; } else { // case2: 叔叔不存在或者存在且为黑 (旋转+变色) if (pCur == pParent->_pLeft) { // g // p u // c RotateR(pGrandfather); pParent->_col = BLACK; pGrandfather->_col = RED; } else { // g // p u // c RotateL(pParent); RotateR(pGrandfather); pCur->_col = BLACK; pGrandfather->_col = RED; } break; } } else { Node* pUncle = pGrandfather->_pLeft; // case1:叔叔存在且为红 if (pUncle && pUncle->_col == RED) { // 变色 pParent->_col = pUncle->_col = BLACK; pGrandfather->_col = RED; // 继续往上处理 pCur = pGrandfather; pParent = pCur->_pParent; } else { // 情况二:叔叔不存在或者存在且为黑 // 旋转+变色 // g // u p // c if (pCur == pParent->_pRight) { RotateL(pGrandfather); pParent->_col = BLACK; pGrandfather->_col = RED; } else { // g // u p // c RotateR(pParent); RotateL(pGrandfather); pCur->_col = BLACK; pGrandfather->_col = RED; } break; } } } _pRoot->_col = BLACK; // 根始终为BLACK return true; }
在插入节点后,需要对红黑树进行平衡性检查,主要包括以下步骤:
bool Check(Node* pCur, int blackNum, int refBlackNum) { if (pCur == nullptr) { if (refBlackNum != blackNum) { cout << "黑色节点的数量不相等" << endl; return false; } //cout << blackNum << endl; return true; } if (pCur->_col == RED && pCur->_pParent->_col == RED) { cout << pCur->_kv.first << "存在连续的红色节点" << endl; return false; } if (pCur->_col == BLACK) ++blackNum; return Check(pCur->_pLeft, blackNum, refBlackNum) && Check(pCur->_pRight, blackNum, refBlackNum); } bool IsBalance() { if (_pRoot && _pRoot->_col == RED) return false; int refBlackNum = 0; Node* pCur = _pRoot; while (pCur) { if (pCur->_col == BLACK) refBlackNum++; pCur = pCur->_pLeft; } return Check(_pRoot, 0, refBlackNum); }
通过代码示例,我们可以清晰地看到红黑树的插入操作以及平衡性检查的实现过程。在测试代码中,我们可以通过输出结果验证红黑树的平衡性。
#include <iostream> using namespace std; enum Colour { RED, BLACK }; template<class K, class V> struct RBTreeNode { public: pair<K, V> _kv; Colour _col; RBTreeNode<K, V>* _pLeft; RBTreeNode<K, V>* _pRight; RBTreeNode<K, V>* _pParent; RBTreeNode(const pair<K, V>& kv) // 构造新增节点 :_pLeft(nullptr) , _pRight(nullptr) , _pParent(nullptr) , _kv(kv) , _col(RED) // 默认为红色 {} }; template<class K, class V> class RBTree { typedef RBTreeNode<K, V> Node; public: RBTree() : _pRoot(nullptr) {} // 在RB树中插入值为kv的节点 bool Insert(const pair<K, V>& kv) { // 插入节点 if (_pRoot == nullptr) { _pRoot = new Node(kv); _pRoot->_col = BLACK; return true; } Node* pParent = nullptr; Node* pCur = _pRoot; while (pCur) { pParent = pCur; if (kv.first < pCur->_kv.first) pCur = pCur->_pLeft; // 往左子树查找 else if (kv.first > pCur->_kv.first) pCur = pCur->_pRight; // 往右子树查找 else return false; // 重复值不插入 } // 创建新节点 pCur = new Node(kv); // 红色的 if (kv.first < pParent->_kv.first) pParent->_pLeft = pCur; else pParent->_pRight = pCur; pCur->_pParent = pParent; // 插入节点后,更新 while (pParent && pParent->_col == RED) { Node* pGrandfather = pParent->_pParent; if (pGrandfather == nullptr) break; // 处理根节点 // 找叔叔 if (pParent == pGrandfather->_pLeft) { Node* pUncle = pGrandfather->_pRight; // case1: 叔叔存在且为RED if (pUncle && pUncle->_col == RED) { // 变色 pParent->_col = pUncle->_col = BLACK; pGrandfather->_col = RED; // 继续往上处理 pCur = pGrandfather; pParent = pCur->_pParent; } else { // case2: 叔叔不存在或者存在且为黑 (旋转+变色) if (pCur == pParent->_pLeft) { // g // p u // c RotateR(pGrandfather); pParent->_col = BLACK; pGrandfather->_col = RED; } else { // g // p u // c RotateL(pParent); RotateR(pGrandfather); pCur->_col = BLACK; pGrandfather->_col = RED; } break; } } else { Node* pUncle = pGrandfather->_pLeft; // case1:叔叔存在且为红 if (pUncle && pUncle->_col == RED) { // 变色 pParent->_col = pUncle->_col = BLACK; pGrandfather->_col = RED; // 继续往上处理 pCur = pGrandfather; pParent = pCur->_pParent; } else { // 情况二:叔叔不存在或者存在且为黑 // 旋转+变色 // g // u p // c if (pCur == pParent->_pRight) { RotateL(pGrandfather); pParent->_col = BLACK; pGrandfather->_col = RED; } else { // g // u p // c RotateR(pParent); RotateL(pGrandfather); pCur->_col = BLACK; pGrandfather->_col = RED; } break; } } } _pRoot->_col = BLACK; // 根始终为BLACK return true; } void InOrder() { _InOrder(_pRoot); } bool Check(Node* pCur, int blackNum, int refBlackNum) { if (pCur == nullptr) { if (refBlackNum != blackNum) { cout << "黑色节点的数量不相等" << endl; return false; } //cout << blackNum << endl; return true; } if (pCur->_col == RED && pCur->_pParent->_col == RED) { cout << pCur->_kv.first << "存在连续的红色节点" << endl; return false; } if (pCur->_col == BLACK) ++blackNum; return Check(pCur->_pLeft, blackNum, refBlackNum) && Check(pCur->_pRight, blackNum, refBlackNum); } bool IsBalance() { if (_pRoot && _pRoot->_col == RED) return false; int refBlackNum = 0; Node* pCur = _pRoot; while (pCur) { if (pCur->_col == BLACK) refBlackNum++; pCur = pCur->_pLeft; } return Check(_pRoot, 0, refBlackNum); } private: void _InOrder(Node* root) { if (root == nullptr) return; _InOrder(root->_pLeft); cout << root->_kv.first << endl; _InOrder(root->_pRight); } // 右单旋 void RotateR(Node* pParent) { Node* pSubL = pParent->_pLeft; Node* pSubLR = pSubL->_pRight; // 右旋 pParent->_pLeft = pSubLR; if (pSubLR) pSubLR->_pParent = pParent; pSubL->_pRight = pParent; pSubL->_pParent = pParent->_pParent; pParent->_pParent = pSubL; if (pParent == _pRoot) _pRoot = pSubL; else { if (pSubL->_pParent->_pLeft == pParent) pSubL->_pParent->_pLeft = pSubL; else pSubL->_pParent->_pRight = pSubL; } } // 左单旋 void RotateL(Node* pParent) { Node* pSubR = pParent->_pRight; Node* pSubRL = pSubR->_pLeft; // 左旋 pParent->_pRight = pSubRL; if (pSubRL) pSubRL->_pParent = pParent; pSubR->_pLeft = pParent; pSubR->_pParent = pParent->_pParent; pParent->_pParent = pSubR; if (pParent == _pRoot) _pRoot = pSubR; else { if (pSubR->_pParent->_pLeft == pParent) pSubR->_pParent->_pLeft = pSubR; else pSubR->_pParent->_pRight = pSubR; } } Node* _pRoot; }; void TestRBTree1() { int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14, 23, 2, 0, 19, 234, 63, 1, 90}; RBTree<int, int> t; for (auto e : a) { t.Insert(make_pair(e, e)); // 1、先看是插入谁导致出现的问题 // 2、打条件断点,画出插入前的树 // 3、单步跟踪,对比图一一分析细节原因 cout << e << "->" << t.IsBalance() << endl; } t.InOrder(); cout << t.IsBalance() << endl; }
红黑树:
AVL树:
红黑树应用场景:
本文详细介绍了红黑树的定义、性质、节点结构、插入操作、平衡性检查等内容,并给出了红黑树的代码实现和测试示例。通过对红黑树的插入操作和平衡性检查的实现过程进行分析,展示了红黑树在维护平衡性和高效性方面的特点。此外,还对红黑树与AVL树进行了对比,说明了它们在平衡性、性能和应用场景上的差异。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。