我们知道,二叉搜索树是个很好的数据结构,可以快速地找到一个给定关键字的数据项,并且可以快速地插入和删除数据项。但是二叉搜索树有个很麻烦的问题,如果树中插入的是随机数据,则执行效果很好,但如果插入的是有序或者逆序的数据,那么二叉搜索树的执行速度就变得很慢。因为当插入数值有序时,二叉树就是非平衡的了,排在一条线上,其实就变成了一个链表……它的快速查找、插入和删除指定数据项的能力就丧失了。

为了能以较快的时间 O(logN) 来搜索一棵树,需要保证树总是平衡的(或者至少大部分是平衡的),这就是说对树中的每个节点在它左边的后代数目和在它右边的后代数目应该大致相等。红-黑树的就是这样的一棵平衡树,对一个要插入的数据项,插入例程要检查会不会破坏树的特征,如果破坏了,程序就会进行纠正,根据需要改变树的结构,从而保持树的平衡。那么红-黑树都有哪些特征呢?

1. 红-黑树的特征

它主要有两个特征: 1.节点都有颜色;2.在插入和删除的过程中,要遵循保持这些颜色的不同排列的规则。首先第一个特征很好解决,在节点类中添加一个数据字段,例如 boolean 型变量,以此来表示节点的颜色信息。第二个特征比较复杂,红-黑树有它的几个规则,如果遵循这些规则,那么树就是平衡的。红-黑树的主要规则如下:

1.每个节点不是红色就是黑色的;

2.根节点总是黑色的;

3.如果节点是红色的,则它的子节点必须是黑色的(反之不一定);

4.从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。

在红-黑树中插入的节点都是红色的,这不是偶然的,因为插入一个红色节点比插入一个黑色节点违背红-黑规则的可能性更小。原因是:插入黑色节点总会改变黑色高度(违背规则4),但是插入红色节点只有一半的机会会违背规则3。另外违背规则3比违背规则4要更容易修正。当插入一个新的节点时,可能会破坏这种平衡性,那么红-黑树是如何修正的呢?

2. 平衡性的修正

红-黑树主要通过三种方式对平衡进行修正,改变节点颜色、左旋和右旋。这看起来有点抽象,我们分别来介绍它们。

2.1 变色
改变节点颜色比较容易理解,因为它违背了规则3。假设现在有个节点E,然后插入节点A和节点S,节点A在左子节点,S在右子节点,目前是平衡的。如果此时再插一个节点,那么就出现了不平衡了,因为红色节点的子节点必须为黑色,但是新插的节点是红色的。所以这时候就必须改变节点颜色了。所以我们将根的两个子节点从红色变为黑色(至于为什么都要变,下面插入的时候会详细介绍),将父节点会从黑色变成红色。可以用如下示意图表示一下:

2.2 左旋
通常左旋操作用于将一个向右倾斜的红色链接旋转为向左链接。示意图如下:

2.3 右旋
右旋可左旋刚好相反,这里不再赘述,直接看示意图:

这里主要介绍了红-黑树对平衡的三种修正方式,大家有个感性的认识,那么什么时候该修正呢?什么时候该用哪种修正呢?这将是接下来我们要探讨的问题。

3. 红-黑树的操作

红-黑树的基本操作是添加、删除和旋转。对红-黑树进行添加或删除后,可能会破坏其平衡性,会用到哪种旋转方式去修正呢?我们首先对红-黑树的节点做一介绍,然后分别对左旋和右旋的具体实现做一分析,最后我们探讨下红-黑树的具体操作。

3.1 红-黑树的节点
红-黑树是对二叉搜索树的改进,所以其节点与二叉搜索树是差不多的,只不过在它基础上增加了一个枚举类型变量来表示节点的颜色,具体看RBNode类:

enum Color
{BLACK,RED
};template <class T>
struct RBTNode
{typedef RBTNode<T> Node;Node* _pLeft = nullptr;Node* _pRight = nullptr;Node* _pParent = nullptr;T _data;

3.2 左旋的具体实现
上面对左旋的概念已经有了感性的认识了,这里就不再赘述了,我们从下面的代码中结合上面的示意图,探讨一下左旋的具体实现:

*************对红黑树节点x进行左旋操作 ******************//** 左旋示意图:对节点x进行左旋*     p                       p*    /                       /*   x                       y*  / \                     / \* lx  y      ----->       x  ry*    / \                 / \*   ly ry               lx ly* 左旋做了三件事:* 1. 将y的左子节点赋给x的右子节点,并将x赋给y左子节点的父节点(y左子节点非空时)* 2. 将x的父节点p(非空时)赋给y的父节点,同时更新p的子节点为y(左或右)* 3. 将y的左子节点设为x,将x的父节点设为y*/
private:void leftRotate(RBNode<T> x) {//1. 将y的左子节点赋给x的右子节点,并将x赋给y左子节点的父节点(y左子节点非空时)RBNode<T> y = x.right;x.right = y.left;if(y.left != null) y.left.parent = x;//2. 将x的父节点p(非空时)赋给y的父节点,同时更新p的子节点为y(左或右)y.parent = x.parent;if(x.parent == null) {this.root = y;
//如果x的父节点为空,则将y设为父节点}
else{    if(x == x.parent.left)
//如果x是左子节点x.parent.left = y;
//则也将y设为左子节点elsex.parent.right = y;
//否则将y设为右子节点}//3. 将y的左子节点设为x,将x的父节点设为yy.left = x;x.parent = y;       }

3.3 右旋具体实现
上面对右旋的概念已经有了感性的认识了,这里也不再赘述了,我们从下面的代码中结合上面的示意图,探讨一下右旋的具体实现:

/*************对红黑树节点y进行右旋操作 ******************//** 左旋示意图:对节点y进行右旋*        p                   p*       /                   /*      y                   x*     / \                 / \*    x  ry   ----->      lx  y*   / \                     / \* lx  rx                   rx ry* 右旋做了三件事:* 1. 将x的右子节点赋给y的左子节点,并将y赋给x右子节点的父节点(x右子节点非空时)* 2. 将y的父节点p(非空时)赋给x的父节点,同时更新p的子节点为x(左或右)* 3. 将x的右子节点设为y,将y的父节点设为x*/private void rightRotate(RBNode<T> y) {//1. 将y的左子节点赋给x的右子节点,并将x赋给y左子节点的父节点(y左子节点非空时)RBNode<T> x = y.left;y.left = x.right;if(x.right != null) x.right.parent = y;//2. 将x的父节点p(非空时)赋给y的父节点,同时更新p的子节点为y(左或右)x.parent = y.parent;if(y.parent == null) {this.root = x;
//如果x的父节点为空,则将y设为父节点}
else{if(y == y.parent.right)
//如果x是左子节点y.parent.right = x;
//则也将y设为左子节点elsey.parent.left = x;
//否则将y设为右子节点}//3. 将y的左子节点设为x,将x的父节点设为yx.right = y;y.parent = x;       }

3.4 插入操作
分析完了红-黑树中主要的旋转操作,接下来我们开始分析常见的插入、删除等操作了。这里先分析插入操作。 由于红-黑树是二叉搜索树的改进,所以插入操作的前半工作时相同的,即先找到待插入的位置,再将节点插入,先来看看插入的前半段代码:

public void insert(T key) {RBNode<T> node = newRBNode<T>(key, RED, null, null, null);if(node != null) insert(node);}//将节点插入到红黑树中,这个过程与二叉搜索树是一样的private void insert(RBNode<T> node) {RBNode<T> current = null;
//表示最后node的父节点RBNode<T> x = this.root;
//用来向下搜索用的
//1. 找到插入的位置
while(x != null) {current = x;int cmp = node.key.compareTo(x.key);if(cmp < 0) x = x.left;elsex = x.right;}node.parent = current;
//找到了位置,将当前current作为node的父节点//2. 接下来判断node是插在左子节点还是右子节点if(current != null) {int cmp = node.key.compareTo(current.key);if(cmp < 0)current.left = node;elsecurrent.right = node;}
else{this.root = node;}//3. 将它重新修整为一颗红黑树insertFixUp(node);}

这与二叉搜索树中实现的思路一模一样,这里不再赘述,主要看看方法里面最后一步insertFixUp操作。因为插入后可能会导致树的不平衡,insertFixUp方法里主要是分情况讨论,分析何时变色,何时左旋,何时右旋。我们先从理论上分析具体的情况,然后再看insertFixUp方法的具体实现。

如果是第一次插入,由于原树为空,所以只会违反红-黑树的规则2,所以只要把根节点涂黑即可;如果插入节点的父节点是黑色的,那不会违背红-黑树的规则,什么也不需要做;但是遇到如下三种情况时,我们就要开始变色和旋转了:

1.插入节点的父节点和其叔叔节点(祖父节点的另一个子节点)均为红色的;
2.插入节点的父节点是红色,叔叔节点是黑色,且插入节点是其父节点的右子节点;
3.插入节点的父节点是红色,叔叔节点是黑色,且插入节点是其父节点的左子节点。

下面我们先挨个分析这三种情况都需要如何操作,然后再给出实现代码。

情况1:插入节点的父节点和其叔叔节点(祖父节点的另一个子节点)均为红色的。此时,肯定存在祖父节点,但是不知道父节点是其左子节点还是右子节点,但是由于对称性,我们只要讨论出一边的情况,另一种情况自然也与之对应。这里考虑父节点是祖父节点的左子节点的情况,如下左图所示:

于这种情况,我们要做的操作有:将当前节点(4)的父节点(5)和叔叔节点(8)涂黑,将祖父节点(7)涂红,变成上右图所示的情况。再将当前节点指向其祖父节点,再次从新的当前节点开始算法(具体等下看下面的程序)。这样上右图就变成了情况2了。

情况2:插入节点的父节点是红色,叔叔节点是黑色,且插入节点是其父节点的右子节点。我们要做的操作有:将当前节点(7)的父节点(2)作为新的节点,以新的当前节点为支点做左旋操作。完成后如左下图所示,这样左下图就变成情况3了。

情况3:插入节点的父节点是红色,叔叔节点是黑色,且插入节点是其父节点的左子节点。我们要做的操作有:将当前节点的父节点(7)涂黑,将祖父节点(11)涂红,在祖父节点为支点做右旋操作。最后把根节点涂黑,整个红-黑树重新恢复了平衡,如右上图所示。至此,插入操作完成!

我们可以看出,如果是从情况1开始发生的,必然会走完情况2和3,也就是说这是一整个流程,当然咯,实际中可能不一定会从情况1发生,如果从情况2开始发生,那再走个情况3即可完成调整,如果直接只要调整情况3,那么前两种情况均不需要调整了。故变色和旋转之间的先后关系可以表示为:变色->左旋->右旋。

至此,我们完成了全部的插入操作。下面我们看看insertFixUp方法中的具体实现(可以结合上面的分析图,更加利与理解):

private void insertFixUp(RBNode<T> node) {  RBNode<T> parent, gparent;
//定义父节点和祖父节点  //需要修整的条件:父节点存在,且父节点的颜色是红色  while(((parent = parentOf(node)) != null) && isRed(parent)) {  gparent = parentOf(parent);
//获得祖父节点  //若父节点是祖父节点的左子节点,下面else与其相反  if(parent == gparent.left) {                  RBNode<T> uncle = gparent.right;
//获得叔叔节点  //case1: 叔叔节点也是红色  if(uncle != null && isRed(uncle)) {  setBlack(parent);
//把父节点和叔叔节点涂黑  setBlack(uncle);  setRed(gparent);
//把祖父节点涂红  node = gparent;
//将位置放到祖父节点处  continue;
//继续while,重新判断  }  //case2: 叔叔节点是黑色,且当前节点是右子节点  if(node == parent.right && isBlack(uncle)) {  leftRotate(parent);
//从父节点处左旋  RBNode<T> tmp = parent;
//然后将父节点和自己调换一下,为下面右旋做准备  parent = node;  node = tmp;  }  //case3: 叔叔节点是黑色,且当前节点是左子节点  if(node == parent.right && isBlack(uncle)) {  setBlack(parent);  setRed(gparent);  rightRotate(gparent);  } }else{ //若父节点是祖父节点的右子节点,与上面的完全相反,本质一样的  RBNode<T> uncle = gparent.left;  //case1: 叔叔节点也是红色  if(uncle != null && isRed(uncle)) {  setBlack(parent);  setBlack(uncle);  setRed(gparent);  node = gparent;  continue;  }  //case2: 叔叔节点是黑色的,且当前节点是左子节点  if(node == parent.left) {  rightRotate(parent);  RBNode<T> tmp = parent;  parent = node;  node = tmp;  }  //case3: 叔叔节点是黑色的,且当前节点是右子节点  setBlack(parent);  setRed(gparent);  leftRotate(gparent);  }  }  //将根节点设置为黑色  setBlack(this.root);  }  //代码仅供参考,提供思路

红黑树的迭代器

迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代器,需要考虑以前问题:
       begin()与end()STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置,end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块?能否给成nullptr呢?

答案是行不通的,因为通过前面的学习知道,对end()位置的迭代器进行operator–操作,必须要能找最后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置:

迭代器实现

1.self& operator++():

++操作一定是要找到下一个比Node大的节点。分两种情况讨论:

  1. 如果右子树存在:找到右子树中的最左节点;

  2. 如果右子树不存在,就向上反

    PNode pParent = _pNode->_pParent;
    while(pParent->_pRight == _pNode)
    {
    _pNode = pParent;
    pParent = _pNode->_pParent;
    }

_pNode = pParent;

2.self& operator--()

分三种情况讨论:
1. _pNode 在head的位置
_pNode 在head的位置,–应该将_pNode放在红黑树中最大节点的位置

if(_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)_pNode = _pNode->_pRight;

2. _pNode 左子树存在
在左子树中找最大的节点即可,最大的节点即左子树中最右侧节点

_pNode = _pNode->_pLeft;
while(_pNode->_pRight)
_pNode = _pNode->_pRight;
  1. _pNode 左子树不存在
PNode pParent = _pNode->_pParent;
while(_pNode == pParent->_pLeft)
{_pNode = pParent;pParent = _pNode->_pParent;
}
_pNode = pParent;

迭代器的完整实现

template<class ValueType, class Ptr, class Ref>
struct RBTreeIterator
{typedef RBTreeNode<ValueType> Node;
typedef Node* PNode;
typedef RBTreeIterator<ValueType, Ptr, Ref> Self;
public:
RBTreeIterator()
: _pNode(nullptr)
{}
RBTreeIterator(PNode pNode)
: _pNode(pNode)
{}
RBTreeIterator(const Self& s)
: _pNode(s._pNode)
{}
Ref operator*()
{return _pNode->_data;
}
Ptr operator->()
{return &(operator*());
}
Self& operator++()
{Increasement();
return *this;
}
Self operator++(int)
{Self temp(*this);
Increasement();
return temp;
}
Self& operator--()
{Decreasement();
return *this;
}
Self operator--(int)
{Self temp(*this);
Decreasement();
return temp;
}
bool operator!=(const Self& s)
{return _pNode != s._pNode;
}
bool operator==(const Self& s)
{return _pNode == s._pNode;
}
// 找迭代器的下一个节点,下一个节点肯定比其大
void Increasement()
{/*
分两种情况讨论:
6. _pNode的右子树存在
7. _pNode的右子树不存在
*/
// 1. _pNode的右子树存在,则在右子树中找最小(最左侧)的节点
if(_pNode->_pRight)
{// 右子树中最小的节点,即右子树中最左侧节点
_pNode = _pNode->_pRight;
while(_pNode->_pLeft)
_pNode = _pNode->_pLeft;
}
else
{/*
8. _pNode的右子树不存在
一般情况情况: 特殊情况:迭代器在根节点位置,并且根节点无右孩子
[head] head
| |
8 8
/ \ / \
/ \ / \
4 9 4 nul
/ \ / \ / \
/ \ / \ / \
nul 5 nul nul nul nul
/ \
/ \
nul nul
一般情况:
如果_pNode在节点5的位置,该节点没有右子树,只能向上方去查找,可能需要找多次:
_pNode-->5 pParent-->4 _pNode == pParent->right
_pNode-->4 pParent-->8 _pNode != pParent->right
结束后:需要将_pNode置于pParent的位置
特殊情况:
对于节点8,最后应该将其放在head的位置
_pNode-->8 pParent-->head _pNode == pParent->right
_pNode-->head pParent-->8 _pNode != pParent->right
结束后:_pNode即为所求取位置,不需将_pNode置于pParent的位置
*/
// 向上查找,直到_pNode != pParent->right
PNode pParent = _pNode->_pParent;
while(pParent->_pRight == _pNode)
{_pNode = pParent;
pParent = _pNode->_pParent;
}
// 特殊情况:根节点没有右子树
if(_pNode->_pRight != pParent)
_pNode = pParent;
}
}
// 获取迭代器指向节点的前一个节点
void Decreasement()
{/*
分三种情况讨论:
9. _pNode 在head的位置
10. _pNode 左子树存在
11. _pNode 左子树不存在
*/
// 1. _pNode 在head的位置,--应该将_pNode放在红黑树中最大节点的位置
// _pNode->_pParent->_pParent == _pNode 可判断出_pNode在根或者head
// _pNode->_color == RED 可排除根节点
if(_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)
_pNode = _pNode->_pRight;
else if(_pNode->_pLeft)
{// 2. _pNode的左子树存在
// 在左子树中找最大的节点即可,最大的节点即左子树中最右侧节点
_pNode = _pNode->_pLeft;
while(_pNode->_pRight)
_pNode = _pNode->_pRight;
}
else
{/*
12. _pNode的左子树不存在
head
|
4
/ \
/ \
3 8
/ \ / \
/ \ / \
nul nul 7 nul
/ \
/ \
nul nul
_pNode如果在节点7的位置,该节点没有左子树,只能向上找
pNode-->7 pParent-->8 _pNode == pParent->_pLeft
_pNode-->8 pParent-->4 _pNode != pParent->_pLeft
结束后:需将_pNode置于pParent的位置
_pNode如果在节点3的位置,注意3是红黑树中最小的节点,不能进行--操作,因此该种
情
况不需要考虑
*/
PNode pParent = _pNode->_pParent;
while(_pNode == pParent->_pLeft)
{_pNode = pParent;
pParent = _pNode->_pParent;
}
_pNode = pParent;
}
}

RBT完整代码实现

#include <iostream>
#include <time.h>
using namespace std;enum Color
{BLACK,RED
};template <class T>
struct RBTNode
{typedef RBTNode<T> Node;Node* _pLeft = nullptr;Node* _pRight = nullptr;Node* _pParent = nullptr;T _data;Color _color = RED;
};template <class T>
struct __TreeIterator
{typedef RBTNode<T> Node;typedef Node* pNode;typedef __TreeIterator<T> Self;Node* _node;__TreeIterator(Node* node):_node(node){}T& operator*(){return _node->_data;}T* operator->(){return &operator*();}bool operator!=(Self& it){return _node != it._node;}// ++it;Self& operator++(){if (_node->_pRight){//含有右孩子,找右的最左节点_node = _node->_pRight;while (_node->_pLeft)_node = _node->_pLeft;}else{//没有右孩子pNode parent = _node->_pParent;while (_node == parent->_pRight){_node = parent;parent = parent->_pParent;}_node = parent;}return *this;}Self operator++(int){Self tmp(*this);++(*this);return tmp;}
};template <class K, class V, class KeyOfValue>
class RBTree
{public:typedef RBTNode<V> Node;typedef __TreeIterator<V> iterator;//typedef Node* pNode;typedef Node* PNode;RBTree(){_header = new Node;_header->_pParent = nullptr;_header->_pLeft = _header;_header->_pRight = _header;}iterator begin(){return iterator(_header->_pLeft);}iterator end(){return iterator(_header);}bool Insert(const V& data){if (_header->_pParent == nullptr){pNode root = new Node;root->_data = data;root->_color = BLACK;root->_pParent = _header;_header->_pParent = root;_header->_pLeft = root;_header->_pRight = root;return true;}KeyOfValue keyOfValue;pNode cur = _header->_pParent;pNode parent = nullptr;while (cur){if (keyOfValue(data) > keyOfValue(cur->_data)){parent = cur;cur = cur->_pRight;}else if (keyOfValue(data) < keyOfValue(cur->_data)){parent = cur;cur = cur->_pLeft;}elsereturn false;}pNode newNode = new Node;newNode->_data = data;if (keyOfValue(data) > keyOfValue(parent->_data))parent->_pRight = newNode;elseparent->_pLeft = newNode;newNode->_pParent = parent;cur = newNode;while (cur != _header->_pParent && cur->_pParent->_color == RED){// cur, parent, gParent, unclepNode parent = cur->_pParent;pNode gParent = parent->_pParent;if (gParent->_pLeft == parent){pNode uncle = gParent->_pRight;// u存在且为红if (uncle && uncle->_color == RED){parent->_color = uncle->_color = BLACK;gParent->_color = RED;cur = gParent;}else{//u不存在/  u存在且为黑//旋转,调色//判断是否需要双旋if (cur == parent->_pRight){RotateL(parent);swap(parent, cur);}//右单旋RotateR(gParent);parent->_color = BLACK;gParent->_color = RED;break;}}else{pNode uncle = gParent->_pLeft;//对应if (uncle && uncle->_color == RED){uncle->_color = parent->_color = BLACK;gParent->_color = RED;cur = gParent;}else{//判断是否要双旋if (cur == parent->_pLeft){RotateR(parent);swap(cur, parent);}RotateL(gParent);parent->_color = BLACK;gParent->_color = RED;break;}}}_header->_pParent->_color = BLACK;_header->_pLeft = leftMost();_header->_pRight = rightMost();return true;}pNode leftMost(){pNode cur = _header->_pParent;while (cur && cur->_pLeft)cur = cur->_pLeft;return cur;}pNode rightMost(){pNode cur = _header->_pParent;while (cur && cur->_pRight)cur = cur->_pRight;return cur;}void RotateL(Node* parent){Node* subR = parent->_pRight;Node* subRL = subR->_pLeft;subR->_pLeft = parent;parent->_pRight = subRL;if (subRL)subRL->_pParent = parent;if (parent != _header->_pParent){Node* gParent = parent->_pParent;if (gParent->_pLeft == parent)gParent->_pLeft = subR;elsegParent->_pRight = subR;subR->_pParent = gParent;}else{subR->_pParent = nullptr;_header->_pParent = subR;}parent->_pParent = subR;}void RotateR(Node* parent){Node* subL = parent->_pLeft;Node* subLR = subL->_pRight;//单向链接 subL , parent, subLRsubL->_pRight = parent;parent->_pLeft = subLR;//向上链接subLRif (subLR)subLR->_pParent = parent;//subL,parent->parent 双向链接if (parent != _header->_pParent){Node* gParent = parent->_pParent;if (gParent->_pLeft == parent)gParent->_pLeft = subL;elsegParent->_pRight = subL;subL->_pParent = gParent;}else{_header->_pParent = subL;subL->_pParent = nullptr;}//向上链接parent, subLparent->_pParent = subL;}void _Inorder(pNode root){if (root){_Inorder(root->_pLeft);cout << root->_kv.first << "-->" << root->_kv.second << endl;_Inorder(root->_pRight);}}void Inorder(){_Inorder(_header->_pParent);}bool IsValidRBTree(){PNode pRoot = _header->_pParent;// 空树也是红黑树if (nullptr == pRoot)return true;// 检测根节点是否满足情况if (BLACK != pRoot->_color){cout << "违反红黑树性质二:根节点必须为黑色" << endl;return false;}// 获取任意一条路径中黑色节点的个数size_t blackCount = 0;PNode pCur = pRoot;while (pCur){if (BLACK == pCur->_color)blackCount++;pCur = pCur->_pLeft;}// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数size_t k = 0;return _IsValidRBTree(pRoot, k, blackCount);}bool _IsValidRBTree(PNode pRoot, size_t k, const size_t blackCount){//走到null之后,判断k和black是否相等if (nullptr == pRoot){if (k != blackCount){cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;return false;}return true;}// 统计黑色节点的个数if (BLACK == pRoot->_color)k++;// 检测当前节点与其双亲是否都为红色PNode pParent = pRoot->_pParent;if (pParent && RED == pParent->_color && RED == pRoot->_color){cout << "违反性质三:没有连在一起的红色节点" << endl;return false;}return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&_IsValidRBTree(pRoot->_pRight, k, blackCount);}private:pNode _header;
};//void testRBTree()
//{//  RBTree<int, int> rbtree;
//  rbtree.Insert(make_pair(0, 0));
//  rbtree.Insert(make_pair(1, 0));
//  rbtree.Insert(make_pair(2, 0));
//  rbtree.Insert(make_pair(10, 0));
//  rbtree.Insert(make_pair(3, 0));
//  rbtree.Insert(make_pair(9, 0));
//
//
//
//  rbtree.Inorder();
//  cout << "IsValidRBTree: " << rbtree.IsValidRBTree() << endl;
//}
//
//void testRBTree2()
//{//  srand(time(nullptr));
//  int n;
//  cin >> n;
//  RBTree<int, int> rb;
//  while (n--)
//  {//      int num = rand();
//      //cout << num << " ";
//      rb.Insert(make_pair(num, num));
//  }
//  cout << endl;
//  cout << "IsValidRBTree: " << rb.IsValidRBTree() << endl;
//}//int main()
//{//  testRBTree2();
//  return 0;
//}

Map和Set的封装

1.map

#include "RBTree.h"template <class K, class V>
class Map
{struct MapKeyOfValue{const K& operator()(const pair<K, V>& data){return data.first;}};
public:typedef typename RBTree<K, pair<K, V>, MapKeyOfValue>::iterator iterator;bool Insert(const pair<K, V>& data){return _t.Insert(data);}iterator begin(){return _t.begin();}iterator end(){return _t.end();}
private:RBTree<K, pair<K,V>, MapKeyOfValue> _t;
};

2.set

template <class K>
class Set
{struct SetKeyOfValue{const K& operator()(const K& data){return data;}};
public:typedef typename RBTree<K, K, SetKeyOfValue>::iterator iterator;bool Insert(const K& key){return _t.Insert(key);}iterator begin(){return _t.begin();}iterator end(){return _t.end();}
private:RBTree<K, K, SetKeyOfValue> _t;
};

文章参考来源:https://mp.weixin.qq.com/s?__biz=MzA5MzY4NTQwMA==&mid=2651006931&idx=2&sn=4c3f4440a9f9756e42d4ad51fd8872df&chksm=8bad9424bcda1d32f1fa863cb4a5da3b2174538a575b04b0439954b1d3349c8cf44cd322ff7c&mpshare=1&scene=23&srcid=0627xhrvbCDPJIrpDfJtuf0Z#rd

数据结构学习——RBT(红黑树)以及实现Map和Set相关推荐

  1. 数据结构 - 学习笔记 - 红黑树

    数据结构 - 学习笔记 - 红黑树 定义 简介 知识点 1. 结点属性 2. 前驱.后继 3. 旋转 查找 插入 父结点为黑色 父结点为红色 1. 有4种情形只需要变色(对应234树4结点) 1.1. ...

  2. 数据结构中常见的树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)

    原文:http://blog.csdn.net/sup_heaven/article/details/39313731 数据结构中常见的树(BST二叉搜索树.AVL平衡二叉树.RBT红黑树.B-树.B ...

  3. 数据结构之「红黑树」

    红黑树 红黑树(Red–black tree)是一种自平衡二叉查找树.红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色. 红黑树的特性: 1.节点要么是红色要么就是黑色,不能没有颜色. 2 ...

  4. 数据结构算法学习 之 红黑树

    1.红黑树的特性 (1)每个节点或者是黑色,或者是红色. (2)根节点是黑色. (3)每个叶子节点(NIL)是黑色. [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!] (4)如果一个节 ...

  5. 数据结构拾遗(1) --红黑树的设计与实现(上)

    红黑树是一种自平衡的二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组(C++ STL 中的map/set).它是在1972年由Rudolf Bayer发明的,他称之为" ...

  6. python 红黑树_python学习笔记|红黑树(性质与插入)

    定义 一种含有红黑节点并能自平衡的二叉查找树(BST) 性质 1.每个节点有红/黑标记位 2.根节点是黑色(硬性规定) 3.每个叶子节点(NIL)都是黑色的虚节点(由此引出性质5) 叶子节点 colo ...

  7. MySQL索引数据结构二叉树、红黑树、B-Tree、B+Tree、Hash

    索引:帮助MySQL高效获取数据的有序的数据结构. 假设我们有一张表table,包含Clo1和Clo2两个字段 内存地址 Clo1 Clo2 0x07 1 36 0x5A 2 20 0x7A 3 80 ...

  8. 数据结构与算法 / 红黑树

    一.定义 根节点是黑色的. 叶子节点是空的且是黑色的. 任何相邻的节点不能都为红色. 任意节点到其每个叶子节点的路径上,黑色的节点的数量相同. 二.性质 红黑树解决了 AVL 树增.删时耗时过大的问题 ...

  9. 高级数据结构与算法 | 红黑树(Red-Black Tree)

    文章目录 红黑树 红黑树的概念 红黑树的性质 红黑树与AVL树 红黑树的实现 红黑树的节点 红黑树的插入 红黑树的查找 红黑树的验证 完整代码 红黑树 红黑树的概念 红黑树,是一种二叉搜索树,但在每个 ...

最新文章

  1. oracle cbo 查询展开,Oracle CBO几种基本的查询转换详解
  2. 小图标文字对齐的终极解决方案
  3. 字典树andXOR*
  4. vSphere DRS
  5. c语言现代方法15章答案(自己做的,更新中)
  6. 真彩色图像数据量 计算_军职在线大学计算机基础(自主模式)
  7. 华师网教计算机应用基础作业,华师大网络教育选修课《计算机应用基础统考》平时作业答案(13页)-原创力文档...
  8. java中Assert使用
  9. mongodb的mapReduce查询
  10. 华泰证券:科技无界,赋能金融新生态
  11. 安装win10系统以及升级win10 home至enterprise版本
  12. 批量打印Word文档并记录(一)
  13. lumion6.0的下载和安装教程
  14. 如何像专业人士一样使用 Google 学术搜索
  15. 20寸JAVA16速自行车_健康成长 快乐骑行 JAVA16/20寸儿童自行车介绍
  16. OpenCV批量处理图片
  17. 1.微信公众号开发:申请公众平台测试账号
  18. 经典名句_万金油_新浪博客
  19. vue生命周期和vue请求
  20. layui合并表格的单元格 合并列或行的两种方法

热门文章

  1. Quo Vadis, Action Recognition? A New Model and the Kinetics Dataset I3D论文精读
  2. 《Windows驱动开发技术详解》学习笔记
  3. php Spreadsheet Csv,基于 PhpSpreadsheet 简单 Excel 导入导出
  4. Pandas学习笔记(二)—— Pandas索引
  5. hdu2907 凸包+简单搜索
  6. 4路服务器cpu位置,4路cpu服务器
  7. 前端怎么画三角形_用CSS画一个三角形
  8. android宿舍管理系统源码,基于android的学生宿舍管理系统的实现.doc
  9. java程序两点之间最短路径算法_java 最短路径算法 如何实现有向 任意两点的最短路径...
  10. 2014522420145238《信息安全系统设计基础》实验一 开发环境的熟悉