红黑树是一颗二叉搜索树,每个节点增加了一个保存节点颜色的存储位;可以是红(Red)或黑(Black);红黑树是AVL树的变种, 红黑树通过一些着色法则确保没有一条路径会比其它路径长出两倍,因而达到接近平衡目的。
红黑树的特性:
(1)根节点是黑色。
(2)每个节点或者是黑色,或者是红色。
(3)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
(4)如果一个节点是红色的,则它的子节点必须是黑色的。 (不能有连续的红色节点)
(5)每个叶子节点(NIL)是黑色。 (这里叶子节点,是指为空(NIL或NULL)的叶子节点)
那么为什么满足上面几条特性,红黑树就可以保证最长路径不超过最短路径的两倍?
在红黑树中,很简单就可以知道一条路径上都是黑色节点,那么这路径就是最短路径,那么最长路径呢?要保持上面的规则,那么最长路径就是在每两个黑色节点之间插入红和节点,这条路径最长也就是两倍的最短路径的长度;
探究清楚这个问题后,接着往下走就是红黑树的插入了;
插入分为5中情况:
(1)根节点为空,这种情况是最简单处理的一种,直接插入就行;
if (_root == NULL)
{
_root = new Node(key,value);
_root->_color = BLACK;
return true;
}
(2)插入红色节点,其父亲是黑色,满足红黑树特性,不需要调整;
(3)u存在且为红; 注:图中的gf是grandfather的简写,同理parent-->p,uncle-->u;
此种情况下,u的左右一定为空,p的左右也为空,才能出现这种情况,若有孩子节点,则其必然为黑色,那么p的右孩子必须存在且为黑色,依次类推就会变成另一种情况:
上述解决也不是很麻烦只要将gf颜色变为红,p和u变为黑,此时满足红黑树所有特性;
(4)u不存在或者u存在且为黑色
cur颜色为红是从cur子树中调整来的;
只需要进行简单的右旋,颜色变换,从上图可以清晰的看出,调整之后的树满足红黑树所有特性;
(5)cur为p的右子树;
只需要以p节点为轴左旋转,就会变成情况(4);
至此,插入就结束了;
下面是插入的代码:
bool Insert(const K &key, const V &value)
{
if (_root == NULL)
{
_root = new Node(key,value);
_root->_color = BLACK;
return true;
}
//寻找插入位置
Node *parent = NULL;
Node *cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//此时parent为要插入节点的父亲节点;
cur = new Node(key,value);
if (key < parent->_key)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//分情况进行调整
while (parent && parent->_color == RED)
{
Node *grandFather = parent->_parent;
if (parent == grandFather->_left)
{
Node *uncle = grandFather->_right;
if (uncle && uncle->_color == RED)
{
grandFather->_color = RED;
uncle->_color = parent->_color = BLACK;
cur = parent; //继续向上判断,因为这gF有可能是一颗子树的根;
parent = cur->_parent;
}
else //uncle->_color 为空或者为存在且为黑;
{
if (cur == parent->_right)
{
RotateL(parent);
cur = parent;
parent = cur->_parent;
}
RotateR(grandFather);
grandFather->_color = RED;
parent->_color = BLACK;
break; //此时已调整到c没有连续的红色节点;
}
}
else //右子树
{
Node *uncle = grandFather->_left;
if (uncle && uncle->_color == RED)
{
grandFather->_color = RED;
parent->_color = uncle->_color = BLACK;
cur = parent;
parent = cur->_parent;
}
else
{
if ( cur == parent->_left)
{
RotateR(parent);
cur = parent;
parent = cur->_parent;
}
RotateL(grandFather);
grandFather->_color = RED;
parent->_color = BLACK;
break;
}
}
_root->_color = BLACK;
}
_root->_color = BLACK;
}
红黑树建好之后,需要进行检查每个节点是否正确,有两种办法去检查,第一种调试程序将红黑树画出来进行检查,第二种交给机器进行检查,聪明的我们肯定会交给机器进行检查;
检查时,要根据红黑树的五个特性进行检查
(1)根节点为空是一个红黑树
(2)有一个节点且其颜色为红,不是红黑树;
(3)以上两点均不满足才进行下面的判断,判断是否有两个连续的红色节点,若有为假,反之为真;
(4)如何来判断每条节点路径上黑节点的数目?先随便找出一条路径上黑节点的数目,一般我选择最左路径L,再遍历每条路径得出黑色节点数目与L路径上的节点数目比较,若出现一条不等的路径,立刻返回false,即不是红黑树;
bool IsBalance()
{
if (_root == NULL)
return true;
if (_root->_color == RED)
return false;
int num = 0;
int blackNum = 0;
Node *cur = _root;
while (cur)
{
if (cur->_color == BLACK)
{
blackNum++;
}
cur = cur->_left;
}
return _IsBalance(_root,blackNum,num);
}
protected:
bool _IsBalance(Node *root, const int blackNum, int num)
{
if (root == NULL)
{
if (blackNum != num)
{
cout << "路径上黑节点数目不相等" << endl;
return false;
}
else
{
return true;
}
}
if (root->_color == RED && root->_parent->_color == RED)
{
cout << "连续红节点" << endl;
return false;
}
if (root->_color == BLACK)
{
num++;
}
return _IsBalance(root->_left, blackNum, num) && _IsBalance(root->_right, blackNum, num);
}
下面是具有插入,中序遍历,判断是否红黑树的完整代码
#define _CRT_SECURE_NO_DEPRECATE
#pragma once
#include<iostream>
#include<string>
using namespace std;
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, const K &value)
:_key(key)
, _value(value)
, _color(RED)
, _left(NULL)
, _right(NULL)
, _parent(NULL)
{}
};
template<class K, class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
RBTree()
:_root(NULL)
{}
bool Insert(const K &key, const V &value)
{
if (_root == NULL)
{
_root = new Node(key,value);
_root->_color = BLACK;
return true;
}
//寻找插入位置
Node *parent = NULL;
Node *cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//此时parent为要插入节点的父亲节点;
cur = new Node(key,value);
if (key < parent->_key)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//分情况进行调整
while (parent && parent->_color == RED)
{
Node *grandFather = parent->_parent;
if (parent == grandFather->_left)
{
Node *uncle = grandFather->_right;
if (uncle && uncle->_color == RED)
{
grandFather->_color = RED;
uncle->_color = parent->_color = BLACK;
cur = parent; //继续向上判断,因为这gF有可能是一颗子树的根;
parent = cur->_parent;
}
else //uncle->_color 为空或者为存在且为黑;
{
if (cur == parent->_right)
{
RotateL(parent);
cur = parent;
parent = cur->_parent;
}
RotateR(grandFather);
grandFather->_color = RED;
parent->_color = BLACK;
break; //此时已调整到c没有连续的红色节点;
}
}
else //右子树
{
Node *uncle = grandFather->_left;
if (uncle && uncle->_color == RED)
{
grandFather->_color = RED;
parent->_color = uncle->_color = BLACK;
cur = parent;
parent = cur->_parent;
}
else
{
if ( cur == parent->_left)
{
RotateR(parent);
cur = parent;
parent = cur->_parent;
}
RotateL(grandFather);
grandFather->_color = RED;
parent->_color = BLACK;
break;
}
}
_root->_color = BLACK;
}
_root->_color = BLACK;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
bool IsBalance()
{
if (_root == NULL)
return true;
if (_root->_color == RED)
return false;
int num = 0;
int blackNum = 0;
Node *cur = _root;
while (cur)
{
if (cur->_color == BLACK)
{
blackNum++;
}
cur = cur->_left;
}
return _IsBalance(_root,blackNum,num);
}
protected:
bool _IsBalance(Node *root, const int blackNum, int num)
{
if (root == NULL)
{
if (blackNum != num)
{
cout << "路径上黑节点数目不相等" << endl;
return false;
}
else
{
return true;
}
}
if (root->_color == RED && root->_parent->_color == RED)
{
cout << "连续红节点" << endl;
return false;
}
if (root->_color == BLACK)
{
num++;
}
return _IsBalance(root->_left, blackNum, num) && _IsBalance(root->_right, blackNum, num);
}
void _InOrder(Node *root)
{
if (root == NULL)
{
return;
}
_InOrder(root->_left);
cout << root->_key <<"--"<< root->_color << " ";
_InOrder(root->_right);
}
void RotateR(Node *parent)
{
Node *subL = parent->_left;
Node *subLR = subL->_right;
parent->_left = subLR;
if (subLR != NULL)
subLR->_parent = parent;
Node *grandPa = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (grandPa == NULL)
{
_root = subL;
subL->_parent = NULL;
}
else
{
if (grandPa->_left == parent)
{
grandPa->_left = subL;
subL->_parent = grandPa;
}
else
{
grandPa->_right = subL;
subL->_parent = grandPa;
}
}
}
void RotateL(Node *parent)
{
Node *subR = parent->_right;
Node*subRL = subR->_left;
parent->_right = subRL;
if (subRL != NULL)
subRL->_parent = parent;
Node *grandPa = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (grandPa == NULL)
{
_root = subR;
subR->_parent = NULL;
}
else
{
if (parent == grandPa->_left)
{
grandPa->_left = subR;
}
else
{
grandPa->_right = subR;
}
subR->_parent = grandPa;
}
}
protected:
Node *_root;
};
void TestRBTree()
{
int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
RBTree<int,int> tree;
for (int i = 0; i < 9; i++)
{
tree.Insert(a[i], i);
// cout << tree.IsBalance() << endl;
}
cout << tree.IsBalance() << endl;
tree.InOrder();
}