• C++数据结构(下):模拟实现红黑树


     "除开隐忍当下,与展望未来。我们别无他法"


    (1)红黑树;

    ①红黑树概念:

    红黑树的概念和它名字一样。其本质就是一颗二叉树。

    但在每个节点 增加一存储 颜色(enum)。

    核心:确保最长路径 不超过 最短路径的两倍!

     

    ②红黑树的性质: 

    相对于概念,红黑树的性质是重中之重!

    1.每个节点不是红色就是黑色(这相当屁话).

    2.根节点(root)一定是黑色.

    3.不会存在连续的红色节点.

    4.每条路径的黑色节点相同.

    只有保证上述四点,才能保证红黑树的一条根本性质:

    最长路径 不超过 最短路径的两倍!


    废话不多讲,接下来进行实现。

    (2)红黑树实现 

    ①结构框架:

     这里的红黑树我们选择使用 K,V结构模拟实现。这样更方便 进行map/set的封装

    1. //记录节点的 属性
    2. enum Colour
    3. {
    4. RED,
    5. BLACK
    6. };
    7. template<class K,class V>
    8. struct RBTreeNode
    9. {
    10. RBTreeNode* _left;
    11. RBTreeNode* _right;
    12. RBTreeNode* _parent;
    13. pair _kv;
    14. Colour _col;
    15. RBTreeNode(const pair kv)
    16. :_left(nullptr),
    17. _right(nullptr),
    18. _parent(nullptr),
    19. _kv(kv),
    20. _col(RED) //默认给红色
    21. {}
    22. };
    23. template<class K,class V>
    24. struct RBTree
    25. {
    26. typedef RBTreeNode Node;
    27. RBTree()
    28. :_root(nullptr)
    29. {}
    30. Node* _root;
    31. };

    同前篇AVL树一样,先把比较简单的部分 搭建起来。对于红黑树的删除和AVL树一样,也不会提出来。

    ②查找+销毁;

    简易:

    1. void _Destroy(Node* root)
    2. {
    3. if (root == nullptr)
    4. return;
    5. _Destroy(root->_left);
    6. _Destroy(root->_right);
    7. delete root;
    8. }
    9. ~RBTree()
    10. {
    11. _Destroy(_root);
    12. _root = nullptr;
    13. }
    14. //查找 是 根据key值来的
    15. Node* find(const K& k)
    16. {
    17. Node* cur = _root;
    18. //和AVL 树一样
    19. while (cur)
    20. {
    21. if (k > cur->_kv.first)
    22. {
    23. cur = cur->_right;
    24. }
    25. else if (k < cur->_kv.first)
    26. {
    27. cur = cur->_left;
    28. }
    29. else
    30. {
    31. return cur;
    32. }
    33. }
    34. return nullptr;
    35. }

    ③检查: 

     

    1. bool _CheckBalance(Node* root, int blacknum, int count)
    2. {
    3. //当root走到空 时 也就是统计完了 数字
    4. if (root == nullptr)
    5. {
    6. if (count != blacknum)
    7. {
    8. cout << "黑色节点数不够" << endl;
    9. return false;
    10. }
    11. return true;
    12. }
    13. //连续红色节点
    14. if (root->_col == RED && root->_parent->_col == RED)
    15. {
    16. cout << "连续红色节点" << endl;
    17. return false;
    18. }
    19. if (root->_col == BLACK)
    20. {
    21. count++;
    22. }
    23. return _CheckBalance(root->_left, blacknum, count)
    24. && _CheckBalance(root->_right, blacknum, count);
    25. }
    26. bool CheckBalance()
    27. {
    28. if (_root == nullptr)
    29. return true;
    30. if (_root->_col == RED)
    31. {
    32. cout << "根节点是红色" << endl;
    33. return false;
    34. }
    35. int blacknum = 0;
    36. //我们从最左边
    37. //记录标准值
    38. Node* left = _root;
    39. while (left)
    40. {
    41. if (left->_col == BLACK)
    42. {
    43. ++blacknum;
    44. }
    45. left = left->_left;
    46. }
    47. //其他路径的 黑色节点
    48. int count = 0;
    49. return _CheckBalance(_root,blacknum,count);
    50. }

    ④插入:

    总的来说,就是分为三种情况,且uncle节点是分析的关键。

    1.p为黑色,那么不管在那里插入。都不会有影响。

    2.p为红色,且uncle存在为红色;

          uncle+p -->红色 g--->黑色   如果g为根就停止,不为根 继续向上调整

    3.p为红色,且uncle不存在或者为红色;

                   单旋+变色

               如果cur 与parent 不同向, 双旋+变色 

    此时,我们需要的左右旋 其实已经在AVL树中实现过,并且这里不需要再处理平衡因子了。 

    1. void RotateR(Node* parent)
    2. {
    3. Node* sub = parent->_left;
    4. Node* subLR = sub->_right;
    5. //让subLR去做parent的左
    6. parent->_left = subLR;
    7. //subLR可能不存在
    8. if (subLR)
    9. subLR->_parent = parent;
    10. sub->_right = parent;
    11. //记录 父节点 因为 sub 可能不是唯一独立的树
    12. Node* grandparent = parent->_parent;
    13. parent->_parent = sub;
    14. if (parent == _root)
    15. {
    16. //独立树
    17. _root = sub;
    18. sub->_parent = nullptr;
    19. }
    20. else
    21. {
    22. //子树
    23. //parent的 父亲仍然保留着 节点地址
    24. if (grandparent->_left == parent)
    25. {
    26. grandparent->_left = sub;
    27. }
    28. else
    29. {
    30. grandparent->_right = sub;
    31. }
    32. //更新回来
    33. sub->_parent = grandparent;
    34. }
    35. }
    36. void RotateL(Node* parent)
    37. {
    38. Node* sub = parent->_right;
    39. Node* subRL = sub->_left;
    40. //让subRL 去做parent的右边
    41. parent->_right = subRL;
    42. //仍然注意避坑
    43. if (subRL)
    44. subRL->_parent = parent;
    45. Node* grandparent = parent->_parent;
    46. sub->_left = parent;
    47. parent->_parent = sub;
    48. if (parent == _root)
    49. {
    50. _root = sub;
    51. sub->_parent = nullptr;
    52. }
    53. else
    54. {
    55. if (grandparent->_left == parent)
    56. {
    57. grandparent->_left = sub;
    58. }
    59. else
    60. {
    61. grandparent->_right = sub;
    62. }
    63. sub->_parent = grandparent;
    64. }
    65. }

     

     调整红黑平衡。

    1. //调整红黑色
    2. while (parent && parent->_col == RED)
    3. {
    4. //祖父
    5. Node* grandfather = parent->_parent;
    6. if (grandfather->_left == parent)
    7. {
    8. //uncle 关键
    9. Node* uncle = grandfather->_right;
    10. //uncle 存在且为红
    11. if (uncle && uncle->_col == RED)
    12. {
    13. uncle->_col = parent->_col = BLACK;
    14. grandfather->_col = RED;
    15. //继续往上处理
    16. cur = grandfather;
    17. parent = cur->_parent;
    18. }
    19. else
    20. {
    21. //uncle 不存在
    22. //判断直线和折线
    23. //此时父节点 在左边
    24. if (cur == parent->_left)
    25. {
    26. //单旋
    27. RotateR(grandfather);
    28. //变色
    29. parent->_col = BLACK;
    30. grandfather->_col = RED;
    31. }
    32. else
    33. {
    34. RotateL(parent);
    35. RotateR(grandfather);
    36. cur->_col = BLACK;
    37. grandfather->_col = RED;
    38. }
    39. break;
    40. }
    41. }
    42. else //右边(parent)
    43. {
    44. Node* uncle = grandfather->_left;
    45. if (uncle && uncle->_col == RED)
    46. {
    47. uncle->_col = parent->_col = BLACK;
    48. grandfather->_col = RED;
    49. //继续往上处理
    50. cur = grandfather;
    51. parent = cur->_parent;
    52. }
    53. else
    54. {
    55. //uncle不存在
    56. //parent在右边
    57. if (cur == parent->_right)
    58. {
    59. RotateL(grandfather);
    60. parent->_col = BLACK;
    61. grandfather->_col = RED;
    62. }
    63. else
    64. {
    65. RotateR(parent);
    66. RotateL(grandfather);
    67. cur->_col = BLACK;
    68. grandfather->_col = RED;
    69. }
    70. break; //插入结束
    71. }
    72. }
    73. }

    总结

    红黑树是一个效率很高的树(增删查改可以近似LogN),它可以达到近似平衡的树。

    相比较于AVL树,它在翻转的次数更少。没那么严格。

    在这一方面,翻转的代价很小。

    '

    这就是本篇的主要内容啦。感谢您的阅读

    祝你好远~

     

  • 相关阅读:
    Linux 编写shell脚本记录操作用户日志信息
    圆桌对话 | 详解2022全域营销的应用趋势和机会
    LINUX-多线程同步
    【Helm三部曲】 Helm 简介及安装
    工作利器!熟悉这几款数据流图工具,事半功倍!
    零基础小白如何自学黑客(网络安全)?
    极限五分钟,在宝塔中用 Docker 部署升讯威在线客服系统
    ref实现input自动获取光标并执行多次
    SecureRandom那些事|真伪随机数
    2019-10《信息资源管理 02378》真卷(独家文字版),圈定章节考点+统计真题分布
  • 原文地址:https://blog.csdn.net/RNGWGzZs/article/details/126021110