目录
1 .红黑树
1.1 红黑树的概念
红黑树,是一种二叉搜索树,但在每个节点上增加一个存储位表示节点的位置,可以是Red或Black。通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因此是接近平衡的。
1.2 红黑树的性质
- 每个结点不是红色就是黑色
- 根结点是黑色的
- 如果一个结点时红色的,则它的两个孩子结点是黑色的
- 对于每个节点,从该结点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
- 每个叶子结点都是黑色的(此时的叶子节点指的是空结点)
那么为什么满足上面的性质,红黑树就能保证: 其最长路径中节点个数不会超过最短路径结点个数的两倍?
因为数中最短路径应该是全是黑色结点的路径,最长路径就是路径上都是一黑一红的结点,这样因为两个路径的黑色结点数是相同的,所以最长路径应该是最短路径结点个数的二倍,但是,这两条路径都是最理想的状态,一个红黑树上不一定会存在。
enum Color
{
RED,
BLACK
};
template <class K,class V>
struct RBTreeNode
{
RBTreeNode(const pair<K,V>& kv=pair<K,V>(),const Color& color=RED)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_color(color)
{}
pair<K, V> _kv;
RBTreeNode<K,V>* _left;
RBTreeNode<K,V>* _right;
RBTreeNode<K,V>* _parent;
Color _color;
};
在结点的定义中,为什么要将结点的默认颜色给为红色的?
因为如果新节点的默认颜色的黑色的话,会违反规则4,所以新结点的默认颜色得是红色,如果他的父节点是黑色,则没有出错,如果父节点是红色,则进行旋转处理。
1.4 红黑树结构
为了后续实现关联式容器简单,红黑树的实现中增加一个头节点,因为根节点必须为黑色,为了与根节点进行区别,将头节点给成黑色,并且让头节点的parent指向红黑树的根节点,left指向红黑树中最小的结点,right指向红黑树中最大的结点,如下:
1.5 红黑树的插入操作
红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:
1. 按照二叉搜索树的树规则插入新结点
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv,BLACK);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
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;
}
cur = new RBTree(kv);
cur->_parent = parent;
if (parent->_kv.first < kv.first) parent->_right = cur;
else parent->_left = cur;
return true;
}
2. 新节点插入之后,检测红黑树的性质是否遭到破坏
因为新节点的默认颜色是红色,因此,如果其父结点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的父结点颜色为红色时,就违反了性质三不能有连在一起的红色结点,此时需要对红黑树分情况来讨论:
约定: cur为当前结点,p为父结点,g为祖父结点,u为叔叔结点
- 情况一: cur为红,p为红,g为黑,u存在且为红
注意:此处看到的树,可能是一颗完整的树,也可能是一颗子树
如果g是根结点,调整完之后,需要将g改为黑色
如果g是子树,g一定要有父节点,且g的父节点如果是红色,则需要继续向上调整
cur和p均为红,违反了性质三,此处能否将p直接改为黑?
解决方式: 将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。
- 情况二: cur为红,p为红,g为黑,u不存在或u存在且为黑
说明:u的情况有两种
1. 如果u结点不存在,则cur一定是一个新插入结点,如果cur不是新插入界定啊,则cur和p一定有一个节点的颜色是黑色,就不满足性质四:每条路径黑色节点个数相同
2. 如果u节点存在,则其一定是黑色的,那么cur节点的原来颜色一定是黑色的,现在看到其是红色的原因是因为cur的子树在调整的过程中将cur节点的颜色由黑色改成了红色。
p为g的左孩子,cur为p的左孩子,则进行右单旋,相反,
p为g的右孩子,cur为p的右孩子,则进行左单旋
p、g变色----p变黑,g变红
情况三: cur为红,p为红,g为黑,u不存在或u存在且为黑
p为g的左孩子,cur为p的右孩子,则针对p进行左单旋;相反
p为g的右孩子,cur为p的左孩子,则针对p进行右单旋
这时就转变为了情况二
红黑树插入完整代码
bool Insert(const pair<K, V>& kv)
{
// 二叉搜索树的正常插入流程
if (_root == nullptr)
{
_root = new Node(kv,BLACK);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
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;
}
cur = new RBTree(kv);
cur->_parent = parent;
if (parent->_kv.first < kv.first) parent->_right = cur;
else parent->_left = cur;
// 检查树中红黑树性质是否被破坏了
while (parent->_color == RED)
{
Node* grandparent = parent->_parent;
Node* uncle = nullptr;
// 插入节点在左右情况
if (parent == grandparent->_left)
{
uncle = grandparent->_right;
// 如果 叔叔节点存在且为红,parent和unclue都是红的情况
if (uncle && uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandparent->_color = RED;
cur = grandparent;
parent = cur->_parent;
}
else
{
// 叔叔节点为黑, parent为红和unclue为黑的情况
if (cur == parent->_left)
{
//当cur和parent都在同一侧时 右旋+变色
RotateR(grandparent);
parent->_color = BLACK;
grandparent = RED;
}
else
{
//当cur和parent在不同一侧时 先左旋变为上面情况,然后再右旋解决
RotateL(parent);
RotateR(grandparent);
cur->_color = BLACK;
grandparent = RED;
}
break; // 当u为黑时,旋转结束之后,parent称为了子树的根,且为黑,
//不会出现再出现parent与parent的父节点都为红的冲突,所以可以直接结束
}
}
else
{
uncle = grandparent->_left;
if (uncle && uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandparent->_color = RED;
cur = grandparent;
parent = cur->_parent;
}
else
{
if (cur == parent->_right)
{
RotateL(grandparent);
parent->_color = BLACK;
grandparent = RED;
}
else
{
RotateR(parent);
RotateL(grandparent);
cur->_color = BLACK;
grandparent = RED;
}
break;
}
}
}
return true;
}
1.6 红黑树的验证
红黑树的检验分为两步:
- 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
- 检测其是否满足红黑树的性质
public:
bool IsBalanceTree()
{
return _IsBalanceTree(_root);
}
private:
bool _IsBalanceTree(Node* root)
{
if (root == nullptr) return true;
if (root->_color != BLACK)
{
cout << "违反红黑树性质二: 根节点必须为黑色" << endl;
return false;
}
size_t blackcount = 0;
Node* cur = root;
while (cur)
{
if (cur->_color == BLACK) blackcount++;
cur = cur->_left;
}
size_t k = 0;
return _Frontorder(root, k, blackcount);
}
bool _Frontorder(Node* root, int k, int blackcount)
{
if (root == nullprt)
{
if (k == blackcount) return true;
else
{
cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
return false;
}
}
Node* parent = cur->_parent;
if (parent && parent->_color == RED && cur->_color == RED)
{
cout << "违反性质三:没有连在一起的红色节点" << endl;
return false;
}
if (root->_color == BLACK) k++;
return _Frontorder(root->_left, k, blackcount) &&
_Frontorder(root->_right, k, blackcount);
}
1.7 红黑树的删除
可参考:《算法导论》或者《STL源码剖析》
https://www.cnblogs.com/fornever/archive/2011/12/02/2270692.html
1.8 红黑树与AVL树的比较
红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都是o(log_2 N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行的增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树用的更多。
1.9 红黑树的应用
- C++ STL库------------map/set、multimap/multiset
- jave库
- linux内核
- 其他的一些库
2. 红黑树的迭代器
迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代器,需要考虑以下问题:
- begin()与end()
STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置,end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在那块?能否给成nullptr呢? 答案是行不通的,因为对end()位置的迭代器进行---操作,必须要能找最后一个元素,此处就不行,因此最好的方式将end()放在头节点的位置。
- operator++()与operator--()
Self& operator++()
{
// 右树存在,找右树中的最左节点
if (_node->_right)
{
Node* cur = _node->_right;
while (cur->_left) cur = cur->_left;
_node = cur;
}
else
{
// 右树不存在
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = cur->_parent;
}
// 让end()为nullptr的写法
防止最后一个节点++为end()即nullptr
//if (parent->_parent == cur) _node = nullptr;
//else _node = parent;
// 让end()为头节点的写法
if (cur->_right != parent) _node = parent;
else _node = cur;
}
return *this;
}
Self& operator--()
{
// 当当前位置在head位置时
if (_node->_parent->_parent==_node && _node->_color==RED) // end()情况
{
_node = _node->_right;
}
// 左树存在
else if (_node->_left)
{
Node* cur = _node->_left;
while (cur->_right) cur = cur->_right;
_node = cur;
}
else
{
// 左树不存在
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = cur->_parent;
}
// 让end为nullptr 的写法
//if(parent->_parent==cur && parent->_color==RED) _node=nullptr;
_node = parent;
}
return *this;
}
红黑树完整含头节点代码
#pragma once
#include <iostream>
using namespace std;
enum Color
{
RED,
BLACK
};
template <class T>
struct RBTreeNode
{
RBTreeNode(const T data=T(),const Color& color=RED)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_data(data)
,_color(color)
{}
T _data;
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Color _color;
};
//template<class K,class T>
//const K& KOFT(const pair<K, T>& data)
//{
// return data.first;
//}
template <class T, class Ref, class Ptr>
struct RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef RBTreeIterator<T, T&, T*> Self;
RBTreeIterator(Node* node)
:_node(node)
{}
Self& operator++()
{
// 右树存在,找右树中的最左节点
if (_node->_right)
{
Node* cur = _node->_right;
while (cur->_left) cur = cur->_left;
_node = cur;
}
else
{
// 右树不存在
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = cur->_parent;
}
// 让end()为nullptr的写法
防止最后一个节点++为end()即nullptr
//if (parent->_parent == cur) _node = nullptr;
//else _node = parent;
// 让end()为头节点的写法
if (cur->_right != parent) _node = parent;
else _node = cur;
}
return *this;
}
Self& operator--()
{
// 当当前位置在head位置时
if (_node->_parent->_parent==_node && _node->_color==RED) // end()情况
{
_node = _node->_right;
}
// 左树存在
else if (_node->_left)
{
Node* cur = _node->_left;
while (cur->_right) cur = cur->_right;
_node = cur;
}
else
{
// 左树不存在
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = cur->_parent;
}
// 让end为nullptr 的写法
//if(parent->_parent==cur && parent->_color==RED) _node=nullptr;
_node = parent;
}
return *this;
}
bool operator==(const Self& s)
{
return _node == s._node;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
private:
RBTreeNode<T>* _node;
};
template <class K,class T,class KOFT>
class RBTree
{
typedef RBTreeNode<T> Node;
typedef RBTree<K, T, KOFT> Tree;
public:
typedef RBTreeIterator<T, T&, T*> Iterator;
typedef RBTreeIterator< T, const T&, const T*> const_Iterator;
Iterator Begin()
{
return _header->_left;
}
Iterator End()
{
return _header;
}
Iterator Begin()const
{
return _header->_left;
}
Iterator End() const
{
return _header;
}
RBTree() = default;
RBTree(const Tree& t)
{
_root = Copy(t->_root);
}
Tree& operator=(const Tree t)
{
swap(_root, t->_root);
return *this;
}
~RBTree()
{
Destory(_root);
_root = nullptr;
}
pair<Iterator,bool> Insert(const T& data)
{
// 二叉搜索树的正常插入流程
if (_root == nullptr)
{
_root = new Node(data, BLACK);
_header = new Node(T(), RED);
_header->_parent = _root;
_root->_parent = _header;
_header->_left = LeftMost();
_header->_right = RightMost();
return make_pair(Iterator(_root), true);
}
Node* parent = nullptr;
Node* cur = _root;
KOFT Kof;
while (cur)
{
if (Kof(cur->_data) < Kof(data))
{
parent = cur;
cur = cur->_right;
}
else if (Kof(cur->_data) > Kof(data))
{
parent = cur;
cur = cur->_left;
}
else return make_pair(Iterator(cur), false);
}
cur = new Node(data);
cur->_parent = parent;
if (Kof(parent->_data) < Kof(data)) parent->_right = cur;
else parent->_left = cur;
Node* n = cur;
// 检查树中红黑树性质是否被破坏了
while (parent->_parent->_parent!=parent && parent->_color == RED)
{
Node* grandparent = parent->_parent;
Node* uncle = nullptr;
// 插入节点在左右情况
if ( parent == grandparent->_left)
{
uncle = grandparent->_right;
// 如果 叔叔节点存在且为红,parent和unclue都是红的情况
if (uncle && uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandparent->_color = RED;
cur = grandparent;
parent = cur->_parent;
}
else
{
// 叔叔节点为黑, parent为红和unclue为黑的情况
if (cur == parent->_left)
{
//当cur和parent都在同一侧时 右旋+变色
RotateR(grandparent);
parent->_color = BLACK;
grandparent->_color = RED;
}
else
{
//当cur和parent在不同一侧时 先左旋变为上面情况,然后再右旋解决
RotateL(parent);
RotateR(grandparent);
cur->_color = BLACK;
grandparent->_color = RED;
}
break; // 当u为黑时,旋转结束之后,parent称为了子树的根,且为黑,
//不会出现再出现parent与parent的父节点都为红的冲突,所以可以直接结束
}
}
else
{
uncle = grandparent->_left;
if (uncle && uncle->_color == RED)
{
parent->_color = BLACK;
uncle->_color = BLACK;
grandparent->_color = RED;
cur = grandparent;
parent = cur->_parent;
}
else
{
if (cur == parent->_right)
{
RotateL(grandparent);
parent->_color = BLACK;
grandparent->_color = RED;
}
else
{
RotateR(parent);
RotateL(grandparent);
cur->_color = BLACK;
grandparent->_color = RED;
}
break;
}
}
}
_root->_color = BLACK;
_header->_left = LeftMost();
_header->_right = RightMost();
return make_pair(Iterator(n),true);
}
bool IsBalanceTree()
{
return _IsBalanceTree(_root);
}
Node* Find(const K& key)
{
Node* cur = _root;
KOFT Kof;
while (cur)
{
if (Kof(cur->_data)> key) cur = cur->_left;
else if (Kof(cur->_data) < key) cur = cur->_right;
else return cur;
}
return nullptr;
}
Node* LeftMost()
{
Node* cur = _root;
while (cur->_left) cur = cur->_left;
return cur;
}
Node* RightMost()
{
Node* cur = _root;
while (cur->_right) cur = cur->_right;
return cur;
}
int Size()
{
return _Size(_root);
}
int Height()
{
return _Height(_root);
}
void Inorder()
{
_Inorder(_root);
return;
}
void RotateR(Node* parent)
{
Node* PL = parent->_left;
Node* PLR = PL->_right;
parent->_left = PLR;
if (PLR) PLR->_parent = parent;
PL->_right = parent;
Node* pparent = parent->_parent;
//parent->_parent = PL;
if (pparent == pparent->_parent->_parent && pparent->_color==RED)
{
PL->_parent = _root->_parent;
_root->_parent->_parent = PL;
_root = PL;
}
else
{
if (pparent->_left == parent) pparent->_left = PL;
else pparent->_right = PL;
PL->_parent = pparent;
}
parent->_parent = PL;
}
void RotateL(Node* parent)
{
Node* PR = parent->_right;
Node* PRL = PR->_left;
parent->_right = PRL;
if (PRL) PRL->_parent = parent;
PR->_left = parent;
Node* pparent = parent->_parent;
//parent->_parent = PR;
if (pparent == pparent->_parent->_parent && pparent->_color == RED)
{
PR->_parent = _root->_parent;
_root->_parent->_parent = PR;
_root = PR;
}
else
{
if (parent == pparent->_left) pparent->_left = PR;
else pparent->_right = PR;
PR->_parent = pparent;
}
parent->_parent = PR;
}
void RotateLR(Node* parent)
{
RotateL(parent->_left);
RotateR(parent);
}
void RoatateRL(Node* parent)
{
RotateR(parent->_right);
RotateL(parent);
}
private:
bool _IsBalanceTree(Node* root)
{
if (root == nullptr) return true;
if (root->_color != BLACK)
{
cout << "违反红黑树性质二: 根节点必须为黑色" << endl;
return false;
}
size_t blackcount = 0;
Node* cur = root;
while (cur)
{
if (cur->_color == BLACK) blackcount++;
cur = cur->_left;
}
size_t k = 0;
return _Frontorder(root, k, blackcount);
}
bool _Frontorder(Node* root, int k, int blackcount)
{
if (root == nullptr)
{
if (k == blackcount) return true;
else
{
cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
return false;
}
}
Node* cur = root;
Node* parent = cur->_parent;
if (parent && parent->_color == RED && cur->_color == RED)
{
cout << "违反性质三:没有连在一起的红色节点" << endl;
return false;
}
if (root->_color == BLACK) k++;
return _Frontorder(root->_left, k, blackcount) &&
_Frontorder(root->_right, k, blackcount);
}
int _Size(Node* root)
{
return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
}
int _Height(Node* root)
{
if (root == nullptr) return 0;
/*int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;*/
return max(_Height(root->_left), _Height(root->_right)) + 1;
}
void Destory(Node* root)
{
if (root == nullptr) return;
Destory(root->_left);
Destory(root->_right);
delete root;
}
Node* Copy(Node* root)
{
if (root == nullptr) return nullptr;
Node* N = new Node(root->_date);
N->_left = Copy(root->_left);
N->_right = Copy(root->_right);
return N;
}
void _Inorder(Node* root)
{
if (root == nullptr) return;
_Inorder(root->_left);
cout <<KOFT(root->_data)<< endl;
_Inorder(root->_right);
}
RBTreeNode<T>* _header;
RBTreeNode<T>* _root;
};
4.map的模拟实现
map的底层结构就是红黑树,因此在map中直接封装一颗红黑树,然后将其接口包装下即可
template<class K,class V>
class map
{
public:
struct MapKOFT
{
const K& operator()(const pair<K,V>& data)
{
return data.first;
}
};
typedef typename RBTree<K, pair<const K, V>, MapKOFT>::Iterator iterator;
typedef typename RBTree<K, pair<const K, V>, MapKOFT>::const_Iterator const_iterator;
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
iterator begin() const
{
return _t.Begin();
}
iterator end() const
{
return _t.End();
}
pair<iterator,bool> insert(const pair<K,V>& key)
{
//return _t.Insert(key);
return _t.Insert(key);
}
iterator find(const K& key)
{
return _t.Find(key);
}
V& operator[](const K& key)
{
pair<iterator, bool> it = insert(make_pair(key,V()));
return it.first->second;
}
private:
RBTree<K, pair<const K, V>, MapKOFT> _t;
};
4.set的模拟实现
set的底层为红黑树,同样只需在set内部封装一颗红黑树,即可将容器实现出来
emplate <class K>
class set
{
struct SetKOFT
{
const K& operator()(const K& data)
{
return data; }
};
public:
typedef typename RBTree<K, const K, SetKOFT>::Iterator iterator;
typedef typename RBTree<K, const K, SetKOFT>::const_Iterator const_iterator;
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
const_iterator begin() const
{
return _t.Begin();
}
const_iterator end() const
{
return _t.End();
}
pair<iterator, bool> insert(const K& key)
{
return _t.Insert(key);
}
iterator find(const K& key)
{
return _t.Find(key);
}
private:
RBTree<K, const K, SetKOFT> _t;
};
void Print(const set<int>& s)
{
set<int>::const_iterator it = s.end();
while (it != s.begin())
{
--it;
//*it += 2;
cout << *it << " ";
}
cout << endl;
}