高级数据结构——红黑树

news/2024/12/4 3:32:47/

目录

1. 红黑树的概念

2. 红黑树的性质

3.  红黑树

6. 红黑树的验证

7. 红黑树的删除

8. 红黑树与AVL数的比较

9. 红黑树的应用

10. 完整代码

10.1 RBTree.h

10.2 test.cpp

1. 红黑树的概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍(最长路径不超过最短路径的2倍),因而是接近平衡的。

 同是二叉搜索平衡树,但是AVL树控制的比红黑树严格的多,AVL树要是每个节点的平衡因子绝对值不超过1,就会导致不断的去旋转调整,付出相对较高的代价,而这里红黑树更像是一种近似平衡,条件没有这么苛刻。

如下一棵树,站在红黑树的角度看是平衡的,站在AVL树的角度看就是不平衡的,需要旋转调整:

 但是从搜索效率的角度看AVL树还是好一点,因为它的平衡标准高,就导致其更加平衡,相同数量的节点情况下AVL树的高度会更低,加上存100w个数据,AVL树大概有20层(log100w),而红黑树最坏就能达到40层,显然AVL树的搜索效率高。但是在内存里找20次和找40次没有什么区别,因为CPU足够的快,这里简单提一下。

2. 红黑树的性质

1、每个结点不是红色就是黑色。
2、根节点必须是黑色的
3、如果一个节点是红色的,则它的两个孩子结点是黑色的(没有连续的红色节点
4、对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点(每条路径都包含相同数量的黑色节点
5、每个叶子结点都是黑色的(此处的叶子结点指的是空结点 -》NIL节点)

根据这些规则,红黑树是如何保证最长路径不超过最短路径的2倍的呢?

首先我们根据规则分析得知,我们假设一条路径的黑色节点的个数为N个,则最长路径和最短路径的情况如下:

  • 最短路径:全黑

  • 最长路径:一黑一红间隔

 而这里一黑一红间隔的原因在于红黑树不允许出现连续的红节点,为了能最大程度的保证最长节点数,唯有一黑一红间隔的方式才能达到最长,综上当黑节点个数固定为N时,最短路径节点个数为N,最长路径节点个数为2N。

3.  红黑树

这里节点的实现相较于AVL树我们依旧是创建成KV模型、三叉链结构,唯一有所改变的是这里要通过枚举的方式把红色和黑色定义好,并在节点类内部定义变量_col表示节点颜色,最后记得写上构造函数

enum Colour
{Red,Black,
};
//节点类
template <class K, class V>
struct RBTreeNode
{//三叉链结构RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;//存储的键值对pair<K, V> _kv;//节点的颜色Colour _col;//构造函数RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(Red){}
};

为什么插入的节点在构造函数这里要处理成红色?

  1. 如果处理成黑色,则一定导致新插入节点的那条路径多出一个黑色节点,不再满足各个路径黑色节点个数相同的性质,一定破坏性质4,此时很难维护。
  2. 如果处理成红色,则可能父亲节点也是红色,此时就出现了连续的红色节点,破坏性质3,不过此时我们向上调整即可,但如果父亲节点是黑色,那就无需操作了,不违反任何性质。

综合利弊,插入黑色节点一定会破坏性质4,而插入红色节点可能破坏性质3,因此处理成红色为宜。

5. 红黑树的插入操作

红黑树的插入操作主要分为这几大步骤:

  • 1、一开始为空树,直接new新节点;
  • 2、一开始非空树,寻找插入的合适位置;
  • 3、找到插入的合适位置后,进行父亲与孩子的双向链接;
  • 4、检测新节点插入后,红黑树的性质是否造到破坏。

接下来对其进行逐个分析:

  • 1、一开始为空树,直接new新节点:

因为树为空的,所以直接new一个新插入的节点,将其作为根_root即可,接着更新颜色_col为黑色。

  • 2、一开始非空树,寻找插入的合适位置:

这里和二叉搜索树的寻找合适的插入位置的思想一样,都要遵循以下几步:

  1. 插入的值 > 节点的值,更新到右子树查找
  2. 插入的值 < 节点的值,更新到左子树查找
  3. 插入的值 = 节点的值,数据冗余插入失败,返回false

当循环结束的时候,就说明已经找到插入的合适位置,即可进行下一步链接。

  • 3、找到插入的合适位置后,进行父亲与孩子的双向链接:

注意这里节点的构成为三叉链,因此最后链接后端孩子和父亲是双向链接,具体操作如下:

  1. 插入的值 > 父亲的值,把插入的值链接在父亲的右边
  2. 插入的值 < 父亲的值,把插入的值链接在父亲的左边
  3. 因为是三叉连,插入后记得双向链接(孩子链接父亲)

走到这,说明节点已经插入完毕,接下来就要对红黑树的颜色进行调整了

  • 4、检测新节点插入后,红黑树的性质是否造到破坏:

不是所有的情况都是需要进行调整的,当插入节点的父亲为黑色(新节点的默认颜色是红色),那么就不需要进行调整,因为没有破坏红黑树的任何一条性质。

只有当插入节点的父亲为红色时(新节点的默认颜色也是是红色),才需要进行调整,因为此时插入的节点和父亲都是红色节点,但是红黑树不允许出现连续的红色节点,此时就要进行调整。

注意这里既然插入节点cur的父亲p是红色,那么根据红黑树的性质(根结点是黑色的),其父亲的父亲g也就是祖父必然存在且一定是黑色,那么其父亲的兄弟节点u(可能不存在)也就是新插入节点cur的叔叔。因此我们约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

这里调整的办法主要是看叔叔节点的颜色如何,叔叔节点的不同,会导致三种不同的情况需要调整:

  • 情况一:cur为红,p为红,g为黑,u存在且为红
  • 情况二:cur为红,p为红,g为黑,u不存在
  • 情况三:cur为红,p为红,g为黑,u存在且为黑

接下来分别进行讨论:

  • 情况一:cur为红,p为红,g为黑,u存在且为红

为了避免出现连续的红色节点,我们可以把父节点p变黑,但是为了保证每条路径的黑色节点个数相同,我们需要把祖父节g点变红(不影响其它路径黑节点的个数),再把叔叔节点u变黑。

调整并未结束,此时祖父节点g为红色,但是如果这棵树本就是一颗完整的树呢?也就是g为根节点,那么只需要把节点g变成黑色即可。

如果这棵树是一棵树的子树,那么刚好把祖父节点g作为新插入的节点cur向上继续调整(继续判断父亲、叔叔如何……),直至调整结束。

补充:情况一不关心左右关系,只变色不旋转,所以 p、u是g的左或右是无所谓的,cur是p的左或右也是无所谓的。

接下来分析情况2:

  • 情况二:cur为红,p为红,g为黑,u不存在

 如果节点u不存在,则cur一定是新插入节点,因为如果cur不是新插入节点,则cur和p一定有一个节点的颜色是黑色,就不满足性质4:每条路径黑色节点个数相同。

此时就是一个很经典的右单旋结构(新节点插入较高左子树的左侧)我们可以先对其进行一个右单旋,再来更新颜色。具体步骤如下:

  1. 让祖父g变成父亲p的右子树。
  2. 父亲p作为根节点。
  3. 更新父亲节点p为黑色。
  4. 更新祖父g为红色。

  • 补充:

如若p为g的右孩子,cur为p的右孩子,则针对p做左单旋转,示例:

如若祖孙三代的关系是折线(cur、parent、grandfather这三个结点为一条折现),则我们需要先进行双旋操作,再进行颜色调整,颜色调整后这棵被旋转子树的根是黑色的,因此无需继续往上进行处理。示例:

 综上:

  1. p为g的左,cur为p的左,则进行右单旋 + p变黑,g变红;
  2. p为g的右,cur为p的右,则进行左单旋 + p变黑,g变红;
  3. p是g的左,cur是p的右,则进行左右双旋 + cur变黑, g变红;
  4. p是g的右,cur是p的左,则进行右左双旋 + cur变黑, g变红。

下面进入情况三

  • 情况三:cur为红,p为红,g为黑,u存在且为黑

此情况绝非单独存在,绝不可能是真的新节点cur插入,然后还会出现p为红,g为黑,u存在且为黑的情况,如果存在,那么只能说明先前插入节点或者构造函数就有问题,因为插入前就不符合红黑树的性质啊(每个路径的黑节点个数均相同)。

既然情况三出现了,那么一定是合理的,它就是建立在情况一的基础上继续往上调整从而出现的一种特殊情况,具体咱就是画图演示:

 此时就是很明显的一个情况3了,cur为红,pp为红,gg为黑,u存在且为黑,由此证明,情况三是通过情况一向上继续调整演化出来的。并且此新节点一定是从p和x任意一颗左右子树插入或演化上来的,才引发后续的cur从黑变红。

此时就是一个很经典的右单旋结构(cur在较高左子树的左侧)我们可以先对其进行一个右单旋,再来更新颜色。具体步骤如下:

  1. 让p的右子树变成g的左子树;
  2. 让p变成根节点位置;
  3. p的右子树指向g;
  4. 更新p的颜色为黑色;
  5. 更新g的颜色为红色。

 补充:

如若p为g的右孩子,cur为p的右孩子,则进行左单旋 + 调色,示例:

若祖孙三代的关系是折现(cur、parent、grandfather这三个结点为一条折线),则我们需要先进行双旋操作,再进行颜色调整,颜色调整后这棵被旋转子树的根是黑色的,因此无需继续往上进行处理。示例:

综上:

  • p为g的左,cur为p的左,则进行右单旋 + p变黑,g变红;
  • p为g的右,cur为p的右,则进行左单旋 + p变黑,g变红;
  • p是g的左,cur是p的右,则进行左右双旋 + cur变黑, g变红;
  • p是g的右,cur是p的左,则进行右左双旋 + cur变黑, g变红.

情况二和情况三旋转 + 变色后,这颗子树不违反红黑树规则,相比插入前,且黑色节点的数量不变,不会影响上层,处理结束了。

代码如下:

bool Insert(const pair<K, V>& kv)
{//1、一开始为空树,直接new新节点if (_root == nullptr){_root = new Node(kv);_root->_col = Black;//新插入的节点处理成黑色return true;}//2、寻找插入的合适位置Node* cur = _root;Node* parent = nullptr;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;//插入的值 = 节点的值,数据冗余插入失败,返回false}}//3、找到了插入的位置,进行父亲与插入节点的链接cur = new Node(kv);cur->_col = Red;//插入的节点处理成红色if (parent->_kv.first < kv.first){parent->_right = cur;//插入的值 > 父亲的值,链接在父亲的右边}else{parent->_left = cur;//插入的值 < 父亲的值,链接在父亲的左边}cur->_parent = parent;//三叉链,要双向链接//4、检测新节点插入后,红黑树的性质是否造到破坏while (parent && parent->_col == Red)//存在连续的红色节点{Node* grandfather = parent->_parent;assert(grandfather);//先确保叔叔的位置if (grandfather->_left == parent){Node* uncle = grandfather->_right;//情况一:cur为红,p为红,g为黑,u存在且为红if (uncle && uncle->_col == Red){//变色parent->_col = uncle->_col = Black;grandfather->_col = Red;//继续往上处理cur = grandfather;parent = cur->_parent;}//情况二+情况三:叔叔不存在,或者叔叔存在且为黑else{if (cur == parent->_left)//p为g的左,cur为p的左,则进行右单旋 + p变黑,g变红{//		  g//     p// curRotateR(grandfather);parent->_col = Black;grandfather->_col = Red;}else//p是g的左,cur是p的右,则进行左右双旋 + cur变黑, g变红{//		  g//	 p//	     curRotateLR(grandfather);cur->_col = Black;grandfather->_col = Red;}break;}}else//grandfather->_right == parent{Node* uncle = grandfather->_left;//情况一:cur为红,p为红,g为黑,u存在且为红if (uncle && uncle->_col == Red){//变色parent->_col = uncle->_col = Black;grandfather->_col = Red;//继续往上处理cur = grandfather;parent = cur->_parent;}//情况二+情况三:叔叔不存在,或者叔叔存在且为黑else{if (cur == parent->_right)//p为g的右,cur为p的右,则进行左单旋 + p变黑,g变红{//	g//	   p//	     curRotateL(grandfather);parent->_col = Black;grandfather->_col = Red;}else//p是g的右,cur是p的左,则进行右左双旋 + cur变黑, g变红{//   g//	      p//	curRotateRL(grandfather);cur->_col = Black;grandfather->_col = Red;}break;}}}_root->_col = Black;//暴力处理把根变成黑色return true;
}
//1、左单旋
void RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;Node* ppNode = parent->_parent;//提前保持parent的父亲//1、建立parent和subRL之间的关系parent->_right = subRL;if (subRL)//防止subRL为空{subRL->_parent = parent;}//2、建立subR和parent之间的关系subR->_left = parent;parent->_parent = subR;//3、建立ppNode和subR之间的关系if (parent == _root){_root = subR;_root->_parent = nullptr;}else{if (parent == ppNode->_left){ppNode->_left = subR;}else{ppNode->_right = subR;}subR->_parent = ppNode;//三叉链双向链接关系}
}
//2、右单旋
void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;Node* ppNode = parent->_parent;//1、建立parent和subLR之间的关系parent->_left = subLR;if (subLR){subLR->_parent = parent;}//2、建立subL和parent之间的关系subL->_right = parent;parent->_parent = subL;//3、建立ppNode和subL的关系if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (parent == ppNode->_left){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;//三叉链双向关系}
}
//3、左右双旋
void RotateLR(Node* parent)
{RotateL(parent->_left);RotateR(parent);
}
//4、右左双旋
void RotateRL(Node* parent)
{RotateR(parent->_right);RotateL(parent);
}

6. 红黑树的验证

红黑树的验证主要分为两大步骤:

  • 1、检测其是否满足二叉搜索树(中序遍历是否为有序序列)
  • 2、检测其是否满足红黑树的性质

接下来分别演示:

  • 1、检测其是否满足二叉搜索树(中序遍历是否为有序序列):

这里只需要递归写一个中序遍历,并判断测试用例的结果是否为一个有序序列即可判断二叉搜索树:

//验证是否为一颗搜索二叉树
void InOrder()
{_InOrder(_root);//调用中序遍历子树cout << endl;
}
//中序遍历的子树
void _InOrder(Node* root)
{if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);
}
  • 2、检测其是否满足红黑树的性质:

这里只要判断是否满足红黑树的5大规则即可,具体操作如下:

  • 1、根节点是否为黑色;
  • 2、任意一条路径黑色节点数是否相同(递归每一条和确定的一条比较是否相同);
  • 3、递归检测是否违反性质三从而出现连续的红节点。
bool IsBalanceTree()
{Node* pRoot = _root;// 空树也是红黑树if (pRoot == nullptr)return true;// 检测根节点是否满足情况if (pRoot->_col != Black){cout << "违反红黑树性质二:根节点必须为黑色" << endl;return false;}// 获取任意一条路径中黑色节点的个数-->拿最左路径作为比较基准值size_t blackCount = 0;Node* pCur = pRoot;while (pCur){if (pCur->_col == Black)blackCount++;pCur = pCur->_left;}// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数size_t k = 0;return _IsValidRBTree(pRoot, k, blackCount);
}
bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount)
{//走到null之后,判断k和black是否相等if (pRoot == nullptr){if (k != blackCount){cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;return false;}return true;}// 统计黑色节点的个数if (pRoot->_col == Black)k++;// 检测当前节点与其双亲是否都为红色Node* pParent = pRoot->_parent;if (pParent && pParent->_col == Red && pRoot->_col == Red){cout << "违反性质三:没有连在一起的红色节点,而这里出现了" << endl;return false;}return _IsValidRBTree(pRoot->_left, k, blackCount) && _IsValidRBTree(pRoot->_right, k, blackCount);
}

7. 红黑树的删除

红黑树的删除这里和AVL树一样就不做过多演示了,具体可参考《算法导论》或者《STL源码剖析》。大佬博文:红黑树插入删除操作

8. 红黑树与AVL数的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(logN),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

9. 红黑树的应用

  •  1、C++ STL库 -- map/set、mutil_map/mutil_set
  • 2、Java 库
  • 3、linux内核
  • 4、其他一些库 

10. 完整代码

10.1 RBTree.h

#pragma once
#include<iostream>
#include<queue>
#include<vector>
#include<assert.h>
using namespace std;
enum Colour
{Red,Black,
};
//节点类
template <class K, class V>
struct RBTreeNode
{//三叉链结构RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;//存储的键值对pair<K, V> _kv;//节点的颜色Colour _col;//构造函数RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(Red){}
};
//红黑树的类
template <class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){//1、一开始为空树,直接new新节点if (_root == nullptr){_root = new Node(kv);_root->_col = Black;//新插入的节点处理成黑色return true;}//2、寻找插入的合适位置Node* cur = _root;Node* parent = nullptr;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;//插入的值 = 节点的值,数据冗余插入失败,返回false}}//3、找到了插入的位置,进行父亲与插入节点的链接cur = new Node(kv);cur->_col = Red;//插入的节点处理成红色if (parent->_kv.first < kv.first){parent->_right = cur;//插入的值 > 父亲的值,链接在父亲的右边}else{parent->_left = cur;//插入的值 < 父亲的值,链接在父亲的左边}cur->_parent = parent;//三叉链,要双向链接//4、检测新节点插入后,红黑树的性质是否造到破坏while (parent && parent->_col == Red)//存在连续的红色节点{Node* grandfather = parent->_parent;assert(grandfather);//先确保叔叔的位置if (grandfather->_left == parent){Node* uncle = grandfather->_right;//情况一:cur为红,p为红,g为黑,u存在且为红if (uncle && uncle->_col == Red){//变色parent->_col = uncle->_col = Black;grandfather->_col = Red;//继续往上处理cur = grandfather;parent = cur->_parent;}//情况二+情况三:叔叔不存在,或者叔叔存在且为黑else{if (cur == parent->_left)//p为g的左,cur为p的左,则进行右单旋 + p变黑,g变红{//		  g//     p// curRotateR(grandfather);parent->_col = Black;grandfather->_col = Red;}else//p是g的左,cur是p的右,则进行左右双旋 + cur变黑, g变红{//		  g//	 p//	     curRotateLR(grandfather); cur->_col = Black;grandfather->_col = Red;}break;}}else//grandfather->_right == parent{Node* uncle = grandfather->_left;//情况一:cur为红,p为红,g为黑,u存在且为红if (uncle && uncle->_col == Red){//变色parent->_col = uncle->_col = Black;grandfather->_col = Red;//继续往上处理cur = grandfather;parent = cur->_parent;}//情况二+情况三:叔叔不存在,或者叔叔存在且为黑else{if (cur == parent->_right)//p为g的右,cur为p的右,则进行左单旋 + p变黑,g变红{//	g//	   p//	     curRotateL(grandfather);parent->_col = Black;grandfather->_col = Red;}else//p是g的右,cur是p的左,则进行右左双旋 + cur变黑, g变红{//   g//	      p//	curRotateRL(grandfather); cur->_col = Black;grandfather->_col = Red;}break;}}}_root->_col = Black;//暴力处理把根变成黑色return true;}
//验证是否为一颗搜索二叉树void InOrder(){_InOrder(_root);//调用中序遍历子树cout << endl;}
//验证是否为红黑树bool IsBalanceTree(){Node* pRoot = _root;// 空树也是红黑树if (pRoot == nullptr)return true;// 检测根节点是否满足情况if (pRoot->_col != Black){cout << "违反红黑树性质二:根节点必须为黑色" << endl;return false;}// 获取任意一条路径中黑色节点的个数-->拿最左路径作为比较基准值size_t blackCount = 0;Node* pCur = pRoot;while (pCur){if (pCur->_col == Black )blackCount++;pCur = pCur->_left;}// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数size_t k = 0;return _IsValidRBTree(pRoot, k, blackCount);}//求一棵树的高度void Height(){cout << "最长路径:" << _maxHeight(_root) << endl;cout << "最短路径:" << _minHeight(_root) << endl;}
private://1、左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;Node* ppNode = parent->_parent;//提前保持parent的父亲//1、建立parent和subRL之间的关系parent->_right = subRL;if (subRL)//防止subRL为空{subRL->_parent = parent;}//2、建立subR和parent之间的关系subR->_left = parent;parent->_parent = subR;//3、建立ppNode和subR之间的关系if (parent == _root){_root = subR;_root->_parent = nullptr;}else{if (parent == ppNode->_left){ppNode->_left = subR;}else{ppNode->_right = subR;}subR->_parent = ppNode;//三叉链双向链接关系}}//2、右单旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;Node* ppNode = parent->_parent;//1、建立parent和subLR之间的关系parent->_left = subLR;if (subLR){subLR->_parent = parent;}//2、建立subL和parent之间的关系subL->_right = parent;parent->_parent = subL;//3、建立ppNode和subL的关系if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (parent == ppNode->_left){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;//三叉链双向关系}}//3、左右双旋void RotateLR(Node* parent){RotateL(parent->_left);RotateR(parent);}//4、右左双旋void RotateRL(Node* parent){RotateR(parent->_right);RotateL(parent);}
//中序遍历的子树void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}
//求一棵树的最长路径的子树int _maxHeight(Node* root){if (root == nullptr)return 0;int lh = _maxHeight(root->_left);int rh = _maxHeight(root->_right);return lh > rh ? lh + 1 : rh + 1;}
//求一棵树的最短路径的子树int _minHeight(Node* root){if (root == nullptr)return 0;int lh = _minHeight(root->_left);int rh = _minHeight(root->_right);return lh < rh ? lh + 1 : rh + 1;}
//求是否满足红黑树性质的子树bool _IsValidRBTree(Node* pRoot, size_t k, const size_t blackCount){//走到null之后,判断k和black是否相等if (pRoot == nullptr){if (k != blackCount){cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;return false;}return true;}// 统计黑色节点的个数if (pRoot->_col == Black)k++;// 检测当前节点与其双亲是否都为红色Node* pParent = pRoot->_parent;if (pParent && pParent->_col == Red && pRoot->_col == Red){cout << "违反性质三:没有连在一起的红色节点,而这里出现了" << endl;return false;}return _IsValidRBTree(pRoot->_left, k, blackCount) && _IsValidRBTree(pRoot->_right, k, blackCount);}
public://层序遍历(非必须)vector<vector<int>> levelOrder() {vector<vector<int>> vv;if (_root == nullptr)return vv;queue<Node*> q;int levelSize = 1;q.push(_root);while (!q.empty()){// levelSize控制一层一层出vector<int> levelV;while (levelSize--){Node* front = q.front();q.pop();levelV.push_back(front->_kv.first);if (front->_left)q.push(front->_left);if (front->_right)q.push(front->_right);}vv.push_back(levelV);for (auto e : levelV){cout << e << " ";}cout << endl;// 上一层出完,下一层就都进队列levelSize = q.size();}return vv;}private:Node* _root = nullptr;
};

10.2 test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"RBTree.h"
void TestRBTree1()
{//int a[] = { 1, 2, 3, 4, 5, 6, 7, 8 };int a[] = { 30, 29, 28, 27, 26, 25, 24, 11, 8, 7, 6, 5, 4, 3, 2, 1 };RBTree<int, int> t;for (auto e : a){t.Insert(make_pair(e, e));}t.levelOrder();t.InOrder();t.Height();
}void TestRBTree2()
{const size_t N = 1024 * 1024;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; ++i){//v.push_back(rand());v.push_back(i);}RBTree<int, int> t;for (auto e : v){t.Insert(make_pair(e, e));}//t.levelOrder();//cout << endl;cout << "Ƿƽ? " << t.IsBalanceTree() << endl;t.Height();//t.InOrder();
}
int main()
{//TestRBTree1();TestRBTree2();return 0;
}


http://www.ppmy.cn/news/521543.html

相关文章

51+l298n+步进电机

目标&#xff1a;51298n电机转一圈按键检测 可以完整带动298n模块&#xff0c; //********************************************* //Developer:Saber //Project:Motor Control //Time:2018/4/18 //********************************************* #include <reg52.h> …

uniapp验证码登录

<view click"sendCode" :class"phone.length 11 ? btn2 : " class"btn">获取短信验证码</view> ***************************************************************************************************************************…

hdu 1869

一个最短路径的变形&#xff0c;解决问题的思路也开阔灵活&#xff0c;其实都是万变不离其宗 六度分离 Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 726 Accepted Submission(s): 275 Problem Description …

sgu495

不多提。 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int MAXN100000100; int n,m; double dp[MAXN]; int main() {while(~scanf("%d%d",&n,&m)){double ans1.0;dp[1]1.0;for(int i2;i<m;i)…

UVA1598

思路&#xff1a;每个优先级队列分别存储sell和buy&#xff0c;每个指令后都判断是否产生交易 package test;import java.util.Comparator; import java.util.Iterator; import java.util.PriorityQueue; import java.util.Queue; import java.util.Scanner; import java.util.…

hdu1698

/* 分析&#xff1a; 线段树水题&#xff0c;成段更新成段查询(总共只查询一次)。 线段树学的太菜了&#xff0c;被这水题虐了&#xff0c;弄了一上午- -I 2012-07-10 */ #include"stdio.h"struct segtree {int l,r;int mid;int val;int flag; }T[300011];void build…

HDU 1598

将边先排序&#xff0c;然后从最小的边开始枚举&#xff0c;当发现需要查找的两个点在一个并查集里面的时候就计算差值&#xff0c;并与min比较。 #include <cstdio> #include <iostream> #include <algorithm> #include <cstring>using namespace std…

UVA1599

题目&#xff1a;https://vjudge.net/problem/UVA-1599 思路&#xff1a;先反向做一次bfs&#xff0c;求出各点到终点经过的最少结点数量。然后正向做一次bfs&#xff0c;每次都选取颜色最小的路径&#xff0c;同时要保证距离的值刚好减1&#xff0c;如果有多条路可以走&#…