赞
踩
很多小伙伴为了刷题发愁
今天为大家推荐一款刷题神奇哦:刷题面试神器牛客
各大互联网大厂面试真题。从基础到入阶乃至原理刨析类面试题 应有尽有,赶快来装备自己吧!助你面试稳操胜券,solo全场面试官
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过
对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩
倍,因而是***接近平衡的***。
通过以上的规则,就能保证:
其最长路径中节点个数不会超过最短路径节点个数的两倍
从而达到相对平衡
// 节点的颜色
enum Color{RED, BLACK};
// 红黑树节点的定义
template<class ValueType>
struct RBTreeNode
{
RBTreeNode(const ValueType& data = ValueType(),Color color = RED)
: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
, _data(data), _color(color)
{}
RBTreeNode<ValueType>* _pLeft; // 节点的左孩子
RBTreeNode<ValueType>* _pRight; // 节点的右孩子
RBTreeNode<ValueType>* _pParent; // 节点的双亲(红黑树需要旋转,为了实现简单给出该字
ValueType _data; // 节点的值域
Color _color; // 节点的颜色
};
需要将节点的默认颜色给成红色的,因为红色不影响红黑树的整体结构。
在后续插入的情况,也是一样
- 父亲是黑色,则不用做任何操作即可满足红黑树的结构
- 父亲是红色,出现了连续的红节点,不满足红黑树的结构,需要处理,具体处理情况如下:
- 因为父亲是红色,所以父亲不可能是根,因为不能出现连续的红节点,所以祖父是黑节点,
- 祖父和父亲都确定了,只有祖父的另外一个孩子(叔叔)的颜色没有确定
- 所以我们以叔叔的颜色为特殊情况再以次分析如何处理
因为有连续的红节点,必须要做变色处理了,如何保证在不破坏红黑树的整体结构下来做变色处理呢?
- 假如g为根节点的时候,将其变黑
- 假如g不为根节点的时候,则继续以g为新增节点向上调整
如果p为g的左孩子,cur为p的左孩子,则进行右单旋转,p变黑,g变红
如果p为g的右孩子,cur为p的右孩子,则进行左单旋转,p变黑,g变红
也是cur为红,p为红,g为黑,u不存在/u为黑,但cur的位置发生了变化,如图所示:
如果p为g的左孩子,cur为p的右孩子,则针对p做左单旋转,p旋转后再对g进行右单旋,旋转后将cur变黑,g变红
如果p为g的右孩子,cur为p的左孩子,则针对p做右单旋转,p旋转后再对g进行左单旋,旋转后将cur变黑,g变红
pair<Node*, bool> Insert(const pair<K, V>& kv)
{
//空树的情况
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return make_pair(_root, true);
}
//查找位置插入节点
Node* cur = _root, * parent = _root;
while (cur)
{
if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return make_pair(cur, false);
}
}
//创建链接节点
cur = new Node(kv);
Node* newnode = cur;
if (parent->_kv.first > kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//父节点存在且为红,则需要调整(不能存在连续的红色节点)
while (parent && parent->_col == RED)
{
//此时当前节点一定有祖父节点
Node* granparent = parent->_parent;
//具体调整情况主要看叔叔节点
//分左右讨论
if (parent == granparent->_left)
{
Node* uncle = granparent->_right;
//情况1:叔叔节点存在且为红
if (uncle && uncle->_col == RED)
{
//修改颜色,继续向上检查
granparent->_col = RED;
parent->_col = uncle->_col = BLACK;
cur = granparent;
parent = cur->_parent;
}
else//情况2和3:叔叔节点不存在 或者存在且为黑
{
//单旋(三代节点为斜线)+变色
if (cur == parent->_left)
{
RotateR(granparent);
granparent->_col = RED;
parent->_col = BLACK;
}
else//双旋(三代节点为折线)+变色
{
RotateL(parent);
RotateR(granparent);
cur->_col = BLACK;
granparent->_col = RED;
}
//旋转后不需再向上调整了
break;
}
}
else//parent=grandparent->right
{
Node* uncle = granparent->_left;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
granparent->_col = RED;
cur = granparent;
parent = cur->_parent;
}
else
{
if (cur == parent->_right)
{
RotateL(granparent);
parent->_col = BLACK;
granparent->_col = RED;
}
else
{
RotateR(parent);
RotateL(granparent);
cur->_col = BLACK;
granparent->_col = RED;
}
break;
}
}
}
//确保根节点为黑
_root->_col = BLACK;
return make_pair(newnode, true);
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* parentP = parent->_parent;
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
subR->_left = parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
subR->_parent = parentP;
if (parentP->_left == parent)
{
parentP->_left = subR;
}
else
{
parentP->_right = subR;
}
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* parentP = parent->_parent;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
subL->_parent = parentP;
if (parentP->_left == parent)
{
parentP->_left = subL;
}
else
{
parentP->_right = subL;
}
}
}
检测其是否满足二叉搜索树(中序遍历是否为有序序列)
检测其是否满足红黑树的性质
bool IsRBTree()
{
//空树
if (_root == nullptr)
{
return true;
}
//根节点为黑色
if (_root->_col == RED)
{
cout << "根节点为红色" << endl;
return false;
}
//黑色结点数量各路径上相同
//先走一条得到基准值
int Blacknum = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
Blacknum++;
cur = cur->_left;
}
//检查子树
int i = 0;
return _IsRBTree(_root, Blacknum, i);
}
bool _IsRBTree(Node* root, int blacknum, int count)
{
//递归到空节点
if (root == nullptr)
{
if (blacknum == count)
return true;
cout << "各路径上黑色节点个数不同" << endl;
return false;
}
//子节点为红则检查父节点是否为红(通过父节点检查子节点会遇到空节点)
if (root->_col == RED && root->_parent->_col == RED)
{
cout << "存在连续红色节点" << endl;
return false;
}
//计数黑结点
if (root->_col == BLACK)
count++;
//递归左右子树
return _IsRBTree(root->_left, blacknum, count) && _IsRBTree(root->_right, blacknum, count);
}
红黑树的删除不做讲解,有兴趣可参考:《算法导论》或者《STL源码剖析》
http://www.cnblogs.com/fornever/archive/2011/12/02/2270692.html
http://blog.csdn.net/chenhuajie123/article/details/11951777
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(logN ),红黑树不追求绝对平衡,其
只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多.
下一章我们将会将map/set如何通过红黑树来实现的,敬请期待吧!!
红黑树是一个极其优秀的数据结构,也是面试中比较爱考的。在liunx,c++,java中也有很多的使用。
对于我们这些将来的互联网从业者来说,是一个必须要掌握的数据结构(可以不知道具体的代码实现,但要懂红黑树是如何实现的,以及后来如何封装出map/set的)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。