• C++ RBTree 理论


    目录

    这个性质可以总结为

    红黑树的最短最长路径

    红黑树的路径范围

    code

    结构

    搞颜色

    插入

    插入逻辑

    新插入节点

    思考:2. 检测新节点插入后,红黑树的性质是否造到破坏?

    解决方法

    变色

    旋转+变色

    第三种情况,如果根节点上面还有节点


    这个性质可以总结为

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

    2.根节点是黑色的

    3.不能有两个连续的红色节点  ,即可以出现 红黑  黑黑 不能出现红红

    4.每条路径上的黑色机节点数量不一样

    至于性质5:每个叶子结点都是黑色的,这里的叶子节点并不是真的叶子节点,而是NIL节点,即空节点。如图(a):

    NIL节点有什么作用?如图(a-2),有多少条路径:

    正确答案是有7条。路径路径的判断规则是:从根节点到NULL。

    如果我们把NIL节点标记出来就好找路径了:

    再比如,图(a-3)是否是红黑树

    大致一看好像是,但是把NIL节点标出来之后:

     路径(b)只有两个黑色节点,不满足红黑树的性质,不是红黑树。

    红黑树的最短最长路径

    那么红黑树的最短路径是什么样子的,应该是全黑的最短:

     那最长的路径呢,应该是一黑一红间隔排列的最长:

    根据图(a-1)我们可以看出,最长的路径是最短的路径的2倍。

    ps

    一个红黑树不一定有最长路径,也不一定有最短路径。

    如图(a-2),有最短路径,没有最长路径:

    红黑树的路径范围

    而知道了最短路径,最长路径,剩下的路径都在最短路径,最长路径范围内,可以写为

                                 [n,2*n]
    

    code

    结构

    1. template<class K,class V>
    2. struct RBTreeNode
    3. {
    4. RBTreeNode* _left;
    5. RBTreeNode* _right;
    6. RBTreeNode* parent;
    7. pair;
    8. Color _col;
    9. //初始话列表
    10. RBTreeNode(const pairkv)
    11. :_left(nullptr)
    12. ,_right(nullptr)
    13. , _parent(nullptr)
    14. , pair
    15. ,_col(RED)
    16. {}
    17. };

    搞颜色

    1. enum Color
    2. {
    3. RED,
    4. BALACK
    5. };

    1. template<class K,class V>
    2. class RBTree
    3. {
    4. typedef RBTreenode Node;
    5. public:
    6. private:
    7. Node* _root = nullptr;
    8. };

    插入

    插入逻辑

    如果节点为空,就给黑色。如果节点不为空,就插入值。

    这个值如果比根节点小,就往左边插入,否则就往右边插入。

    1. bool Insert(const pair& kv)
    2. {
    3. if (_root == nullptr)
    4. {
    5. _root = new(kv);
    6. _root->_col = BALACK;
    7. return true;
    8. }
    9. //初始化父亲节点和根节点
    10. Node* parent = nullptr;
    11. Node* cur = _root;
    12. while (cur)
    13. {
    14. //key值大,往右走
    15. if (cur->kv.first < kv.first)
    16. {
    17. cur = cur->right;
    18. }
    19. //key值小,往左走
    20. else if (cur->kv.first > kv.first)
    21. {
    22. cur = -cur->left;
    23. }
    24. //否则key值和当前节点相等,不插入
    25. else
    26. {
    27. return false;
    28. }
    29. }
    30. //找到了返回true1
    31. return true;
    32. }

    新插入节点

    思考:2. 检测新节点插入后,红黑树的性质是否造到破坏?

    如图(b-1),现在要插入一个节点,那么是插入一个黑色节点还是红色节点呢?

    如果插入黑色节点,那么该路径就会多一个黑色节点,根据红黑树特性,其他路径都要补一棵黑色节点,

    如果插入红色节点,则只会影响父节点

    (即

    1.如果父节点也会红节点。两个红节点不能紧挨,需调整

    2.如果父亲节点是黑色,则不需调整,直接插入。)。

    我们看一下怎么调整,如图(b-2),新插入了一个红色节点7:

    解决方法

    能变色先变色,变色完之后还不行再旋转

    变色

    如图(b-3),先把父节点8变黑:

    这个时候该路径就多了一个黑色节点,再变图(b-4)把6节点变红:

    这个时候该路径又少了个黑色节点,再变图(b-5) 把5节点变黑:

    旋转+变色

    第二种情况,例如图(b-6):如果还是把父节点变为黑色,把6节点变为红色,那么其他路径就会多一个黑色节点。

    而该路径又没有其他节点可以再变黑色来平衡这种状态,所以靠变色解决不了这个问题。

    这个时候就要旋转了。

    先右旋为图(c-1):

    再左旋为图(c-2):

    然后再变色为图(c-4):

    情况一: cur为红,p为红,g为黑,u存在且为红

    解决:叔叔存在,  变色

    如图(d-0),新插入了一个节点cur:

    cur为红色节点,那就需要调整。

    把p节点变为黑色节点,那么u节点也要变为黑色节点,那么此时就要把g节点变为红色节点。也就是图(d-1):

    为什么要把g节点变为红色节点呢?

    假设g节点不变为红色也就是图(d-3):

    由图(d-1)变为图(d-3)我们发现每条路径凭空多了1个黑色节点。

    g节点上面还有节点,那么多了个黑色节点,就会影响上面的路径,所以需要把g节点变红来平衡一下。

    如图(d-1):

    这个时候万一g节点的父节点是红色节点,如图(d-4):
    两个红色节点不能连续,还要调整,如果g节点的父亲节点为黑色,如图(d-5),那就不需要再调整:

     新增节点给红色:

    1. cur = new Node(kv);
    2. cur->_col = RED;
    3. if (parent->_kv.first < kv.first)
    4. {
    5. paret->_right = cur;
    6. cur->_parent = parent;
    7. }
    8. else if (_parent->_kv.first > kv.first)
    9. {
    10. parent->_left = cur;
    11. cur->_parent = parent;
    12. }

    父亲节点是红色就调整,是黑色就不用调整:

    1. while (parent->_col = RED)
    2. {
    3. }

    父亲节点可能在左边(e-1),也可能在右边(e-2):但是不论父亲节点在左在右,父亲节点的父亲父亲节点肯定是granparent节点。

    先说图(e-1)的情况(即父亲节点在左):

    按照之前的推演,应该先把父亲节点和叔叔节点变为黑色,然后为了防止影响了上面的节点,还要把grandparent节点变为红色:

    1. while (parent&& parent->_col = RED) //当父亲为红色时
    2. {
    3. Node* grandparent = parent->_parent;//祖父节点是父亲节点的父亲节点
    4. if (parent = grandparent->_left)//第一种情况,叔叔节点在右时
    5. {
    6. Node* uncle = grandparent->_right;//叔叔节点在祖父节点的右边
    7. if (unlce && unluce->_col == RED)
    8. {
    9. uncle->_col = parent->_col = BLACK;//叔叔节点的颜色要变成黑色
    10. grandparent->_col = RED;//祖父节为了平衡,要把父亲节的颜色变为红色

    即变为图(d-1):

    此时又有两种情况  (d-4),(d-5):

     (d-4)(d-5)的情况会在后续处理。

    继续向上处理

    这个时候把祖父节点当做当前节点,让祖父节点去找它的父亲

    1. }
    2. while (parent&& parent->_col = RED) //当父亲为红色时
    3. {
    4. Node* grandparent = parent->_parent;//祖父节点是父亲节点的父亲节点
    5. if (parent = grandparent->_left)//第一种情况,叔叔节点在右时
    6. {
    7. Node* uncle = grandparent->_right;//叔叔节点在祖父节点的右边
    8. if (unlce && unluce->_col == RED)
    9. {
    10. uncle->_col = parent->_col = BLACK;//叔叔节点的颜色要变成黑色
    11. grandparent->_col = RED;//祖父节为了平衡,要把父亲节的颜色变为红色
    12. //继续向上处理
    13. cur = grandparent; //把祖父节点当做当前节点
    14. parent = cur->parent; //祖父节点去找它的父亲
    15. }
    16. }
    17. }

    这个时候万一祖父节点向上不再有节点,祖父节点就是最终节点怎么办?

    祖父节点若上面没有节点,那么祖父节点就是作为根节点,根节点不能为红,把根节点再变黑。(r如图(D-5):

    1. while (parent->_col = RED)
    2. {
    3. Node* grandparent = parent->_parent;//祖父节点是父亲节点的父亲节点
    4. if (parent = grandparent->_left)//第一种情况,父亲节点是左子树,叔叔节点是右子树
    5. {
    6. Node* uncle = grandparent->_right;//叔叔节点在祖父节点的右边
    7. uncle->_col=parent->_col= BLACK;//叔叔节点的颜色要变成黑色
    8. grandparent->_col = RED;//祖父节为了平衡,要把父亲节的颜色变为红色
    9. }
    10. //把祖父当成当前节点,继续向上处理
    11. cur = grandparent;
    12. }
    13. //祖父节点向上不再有节点,祖父节点作为根节点,必须为黑色
    14. _root->_col = BACK;

    情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑

    解决:把祖父右旋走 ,让父亲做新根,根就要为黑色节点,再把父亲变黑:

    图(0-0)到(0-1)为演变过程:

    1. while (parent&& parent->_col = RED) //当父亲为红色时
    2. {
    3. Node* grandparent = parent->_parent;//祖父节点是父亲节点的父亲节点
    4. if (parent = grandparent->_left)//第一种情况,叔叔节点在右时
    5. {
    6. Node* uncle = grandparent->_right;//叔叔节点在祖父节点的右边
    7. if (unlce && unluce->_col == RED)
    8. {
    9. uncle->_col = parent->_col = BLACK;//叔叔节点的颜色要变成黑色
    10. grandparent->_col = RED;//祖父节为了平衡,要把父亲节的颜色变为红色
    11. //继续向上处理
    12. cur = grandparent; //把祖父节点当做当前节点
    13. parent = cur->parent; //祖父节点去找它的父亲
    14. }
    15. else //第二种情况:叔叔节点在左边
    16. {
    17. if (cur == parent->left) //当前节点在父亲节点的左边
    18. { //单旋+变色
    19. RotateR(granparent); //旋转:把祖父右旋走,让父亲做新根
    20. parent->_col = BLACK; //变色:做新根就要为黑色节点
    21. grandparent->_col = RED; //祖父为了平衡变红
    22. }
    23. }
    24. }
    25. }
    26. //祖父节点向上不再有节点,祖父节点作为根节点,必须为黑色
    27. _root->_col = BLACK;

    情况三:p为g的左孩子,cur为p的右孩子(如图e-1)

    解决方案

    (A)p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,
    (B)p为g的右孩子,cur为p的左孩子,则针对p做右单旋转

    图(e-1)符合(A)解决方案,以下是对图(e-1)用(A)方案进行推演的过程:

    else 父亲在右边

    如图(f-3):

    1. while (parent&& parent->_col == RED) //当父亲为红色时
    2. {
    3. Node* grandparent = parent->_parent;//祖父节点是父亲节点的父亲节点
    4. if (parent = grandparent->_left)//第一种情况,叔叔节点在右时
    5. {
    6. Node* uncle = grandparent->_right;//叔叔节点在祖父节点的右边
    7. if (uncle && uncle->_col == RED)
    8. {
    9. uncle->_col = parent->_col = BLACK;//叔叔节点的颜色要变成黑色
    10. grandparent->_col = RED;//祖父节为了平衡,要把父亲节的颜色变为红色
    11. //继续向上处理
    12. cur = grandparent; //把祖父节点当做当前节点
    13. parent = cur->_parent; //祖父节点去找它的父亲
    14. }
    15. else //第二种情况:叔叔节点在左边
    16. {
    17. if (cur == parent->_left) //当前节点在父亲节点的左边
    18. { //单旋+变色
    19. RotateR(grandparent); //旋转:把祖父右旋走,让父亲做新根
    20. parent->_col = BLACK; //变色:做新根就要为黑色节点
    21. grandparent->_col = RED; //祖父为了平衡变红
    22. }
    23. else //当前节点在父亲节点右边
    24. { //双旋
    25. RotateL(parent); //父亲右旋
    26. RotateR(grandparent); //祖父右旋
    27. cur->_col = BLACK; //当前节点变黑
    28. grandparent->_col = RED; //祖变红
    29. }
    30. break;
    31. }
    32. }
    33. else //父亲在右边
    34. {
    35. Node* uncle = grandparent->_left;//叔叔节点在祖父节点的右边
    36. if (uncle = grandparent->_left) //叔叔在左边
    37. //变色
    38. if (uncle && uncle->_col == parent->_col == RED) //if叔叔存在且颜色为红色
    39. {
    40. uncle->_col = parent->_col = BLACK; //叔叔父亲都变黑
    41. grandparent->_col = RED; //祖父上面还有节点,要变红
    42. //继续向上处理
    43. cur = grandparent;
    44. }
    45. else //叔叔颜色为黑色
    46. {
    47. if (cur = parent->_right) //叔叔在右边
    48. {
    49. RotateL(grandparent); //左旋爷
    50. parent->_col = BLACK;
    51. grandparent->_col = RED; //祖变红
    52. }
    53. else //叔叔在左边
    54. {
    55. // g
    56. // u p
    57. // c
    58. RotateR(parent); // 父亲右旋
    59. RotateL(grandparent); //祖父旋走,让cur当根
    60. cur->_col = BLACK; //根变黑色
    61. grandparent->_col = RED; //祖父为了平衡变红色
    62. }
    63. }
    64. }
    65. }
    66. //祖父节点向上不再有节点,祖父节点作为根节点,必须为黑色
    67. _root->_col = BLACK;
    68. //找到了返回true1
    69. return true;
    70. }

  • 相关阅读:
    算法 含有min函数的栈-(栈)
    大数据组件-Flume集群环境的启动与验证
    Vue 项目开发将数据下载到本地的方法
    亚商投资顾问 早餐FM/0929 国际油价全线上涨
    超全解析:CRM是什么?怎么用?10大好用的CRM系统大盘点!
    spring中注解来创建bean
    syntax error near unexpected token `(‘
    【推荐系统】什么是好的推荐系统?个性化和非个性化推荐
    Harmony OS—UIAbility的使用
    【kafka】基本名词解释
  • 原文地址:https://blog.csdn.net/m0_65143871/article/details/134352331