1 红黑树原理分析
1.1 规则
红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是Red或Black。通过对任何一条从根到叶子简单路径上的颜色来约束,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡。
红黑树是满足下面规则的二叉搜索树:
- 每个节点,不是红色就是黑色的
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个子节点是黑色的
- 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。
- 每个叶子节点都是黑色的(这里的叶子节点是指的NIL节点(空节点))
思考:为什么满足上面的颜色约束性质,红黑树能保证最长路径不超过最短路径的两倍?
最短的路径上节点的颜色全部都为黑色;最长的路径则为黑红交叉的路径,其上有与最短路径的黑节点数目相同的黑节点数数目。所以我们按照红黑树性质所建立的红黑树的最长路径必然不会超过最短路径的两倍!
建立红黑树的节点类:
插入的新节点默认是红色的。原因是:插入黑节点必然会影响所有路径都含有相同数目的黑色节点这一原则,较难维护!
1.2 插入节点的几种情况
注:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
1.2.1 情况一
cur为红,p为红,g为黑,u存在且为红
则将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。
1.2.2 情况二
cur为红,p为红,g为黑,u不存在/u为黑
p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,p为g的右孩子,cur为p的右孩子,则进行左单旋转
p、g变色–p变黑,g变红
1.2.3 情况三
cur为红,p为红,g为黑,u不存在/u为黑
p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,p为g的右孩子,cur为p的左孩子,则针对p做右单旋转
则转换成了情况2
1.3 红黑树判断
判断方法:
- 根结点是否满足红黑树规则,是否为黑色。
- 每条路径的黑色节点相等。统计出一条路径的黑色节点的个数,然后与其他路径黑色节点个数进行比较。
- 不存在连续的红色节点,判断红色节点的父亲节点是否为红色。
2 红黑树代码实现
//RBtree.h
#pragma once
#include<iostream>
#include<stdlib.h>
#include <algorithm>
enum Colour
{
RED,
BLACK,
};
template<class K,class V>
struct RBTreeNode//三叉链
{
RBTreeNode<K,V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
K _key;
V _value;
Colour _col;
RBTreeNode(const K& key = K(), const V& value = V())
:_left(NULL)
,_right(NULL)
,_parent(NULL)
,_key(key)
, _value(value)
,_col(RED)//初始化插入节点的颜色为红色,不影响黑节点个数
{}
};
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->_col = 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;
}
}
cur = new Node(key, value);
if (parent->_key < key)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
// 调平衡
//检查规则:1.parent为黑,不用调整
//2.parent为红,uncle为红
//3.parent为红,uncle为黑或者不存在
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
if (uncle&&uncle->_col == RED)//parent为红,uncle为红
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else//parent为红,uncle为黑或者不存在,分单旋和双旋两种情况
{
if (cur == parent->_right)//双旋
{
RotateL(parent);
swap(cur, parent);
}
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
}
else
{
Node* uncle = grandfather->_left;
if (uncle&&uncle->_col == RED)//parent为红,uncle为红
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else//parent为红,uncle为黑或者不存在,分单旋和双旋两种情况
{
if (cur == parent->_left)//双旋
{
RotateR(parent);
swap(cur, parent);
}
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
}
}
_root->_col = BLACK;
return true;
}
void RotateR(Node* parent)//右旋
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
Node* PPnode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = NULL;
}
else
{
if (PPnode->_left == parent)
{
PPnode->_left = subL;
subL->_parent = PPnode;
}
else
{
PPnode->_right = subL;
subL->_parent = PPnode;
}
}
}
void RotateL(Node* parent)//左旋
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
Node* PPnode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
_root->_parent = NULL;
}
else
{
if (PPnode->_left == parent)
{
PPnode->_left = subR;
subR->_parent = PPnode;
}
else
{
PPnode->_right = subR;
subR->_parent = PPnode;
}
}
}
bool _IsBalance(Node* root, const int & count, int num)
{
if (root->_col == NULL)
return true;
if (root->_col == RED&&root->_parent->_col == RED)//存在两个连续的红节点
return false;
if (root->_col == BLACK)//黑节点就CurBlackNum++
num++;
if (root->_left == NULL&&root->_right == NULL)
{
if (num == count)
{
return true;
}
else
{
return false;//路径上黑节点个数不同返回false
}
}
return _IsBalance(root->_left, count, num) && _IsBalance(root->_right, count, num);
}
bool IsBalance()//是否为红黑树
{
if (_root->_col != BLACK)
{
return false;
}
int count = 0;//统计出一条路径的黑色节点的个数
int num = 0;//需要与count比较的其他路径黑色节点个数
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
{
count++;
}
cur = cur->_right;
}
return _IsBalance(_root, count, num);
}
void _InOrder(Node* root)
{
if (root == NULL)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
Node* _root;
};
//test.cpp
#include"RBtree.h"
using namespace std;
void TestRBTree()
{
int a1[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
RBTree<int, size_t> t1;
for (size_t i = 0; i < sizeof(a1) / sizeof(int); ++i)
{
t1.Insert(a1[i], i);
}
t1.InOrder();
cout << "IsBalance?" << t1.IsBalance() << endl;
}
int main()
{
TestRBTree();
system("pause");
return 0;
}
运行结果:
红黑树的效率:
1)最坏情况下,红黑树高度不超过2lgN
最坏的情况就是,红黑相间的路径长度是全黑路径长度的2倍。
2)红黑树的平均高度大约为lgN
红黑树的运用(高效的二叉搜索树)
红黑树这种数据结构应用十分广泛,在多种编程语言中被用作符号表的实现,如:
- Java中的java.util.TreeMap,java.util.TreeSet
- C++ STL中的:map,multimap,multiset
- .NET中的:SortedDictionary,SortedSet 等
红黑树和AVL树的比较
1)红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都是O(lg(N))
2)红黑树的不追求完全平衡,保证最长路径不超过最短路径的2倍,相对而言,降低了旋转的要求,所以性能优于AVL树,所以实际运用中红黑树更多。红黑树是一种特殊的二叉查找树,他的查找方法也和二叉查找树一样,不需要做太多更改,但是由于红黑树比一般的二叉查找树具有更好的平衡,所以查找起来更快。