• 【C++红黑树】带图详细解答红黑树的插入,测试自己的红黑树是否正确的代码


    目录

    1.红黑树的概念

    1.1红黑树的特性(4+1)

    2.红黑树的框架 

    3.红黑树的插入 

            3.1parent在grandfather的左边

             3.1parent在grandfather的右边

     4.测试自己的红黑树是不是平衡的


    1.红黑树的概念

    红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是RedBlack。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,确保红黑树没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

    • 所以它是一个弱平衡二叉搜索树,AVL1树是一个严格的平衡二叉搜索树

    1.1红黑树的特性(4+1)

    1. 每个结点不是红色就是黑色
    2. 根节点是黑色的
    3. 如果一个节点是红色的,则它的所有的孩子结点是黑色的
    4. 对于每个结点,从该结点到其所有后代叶结点的路径上,均包含相同数目的黑色结点

    每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

    • 我认为这一条只是标记的作用,让我们更好分别每一条路径

     

    2.红黑树的框架 

    1. //枚举颜色
    2. enum Colour
    3. {
    4. RED,
    5. BLACK,
    6. };
    7. template<class K, class V>
    8. struct RBtreeNode
    9. {
    10. RBtreeNode(const pair& kv)
    11. :_left(nullptr)
    12. ,_right(nullptr)
    13. ,_parent(nullptr)
    14. ,_kv(kv)
    15. //初始化给红色,红色比黑色更好处理
    16. ,_col(RED)
    17. {}
    18. //三叉链
    19. RBtreeNode* _left;
    20. RBtreeNode* _right;
    21. RBtreeNode* _parent;
    22. //数据
    23. pair _kv;
    24. //颜色
    25. Colour _col;
    26. };
    27. template<class K,class V>
    28. class RBtree
    29. {
    30. typedef RBtreeNode Node;
    31. public:
    32. RBtree()
    33. :_root(nullptr)
    34. {}
    35. //旋转
    36. void RotateL(Node* parent)
    37. void RotateR(Node* parent)
    38. //插入
    39. pairbool> Insert(const pair kv)
    40. //寻找
    41. Node* Find(const K& key)
    42. //测试自己的写的的红黑树,是否合适
    43. bool CheckBalance()
    44. private:
    45. Node* _root;
    46. };

    3.红黑树的插入 

    1. pairbool> Insert(const pair kv)
    2. //是否为空树
    3. {
    4. if (_root == nullptr)
    5. {
    6. _root = new Node(kv);
    7. _root->_col = BLACK;
    8. return make_pair(_root, true);
    9. }
    10. Node* cur = _root,*parent=_root;
    11. while (cur)
    12. {
    13. if (cur->_kv.first < kv.first)
    14. {
    15. parent = cur;
    16. cur = cur->_right;
    17. }
    18. else if (cur->_kv.first > kv.first)
    19. {
    20. parent = cur;
    21. cur = cur->_left;
    22. }
    23. else
    24. {
    25. return make_pair(cur, false);
    26. }
    27. }
    28. Node* newnode = new Node(kv);
    29. newnode->_col = RED;
    30. if (parent->_kv.first < kv.first)
    31. {
    32. parent->_right = newnode;
    33. newnode->_parent = parent;
    34. }
    35. else
    36. {
    37. parent->_left = newnode;
    38. newnode->_parent = parent;
    39. }
    40. cur = newnode;
    41. while (parent && parent->_col == RED)
    42. {
    43. // 如果父亲存在,且颜色为红色就需要处理
    44. Node* grandfather = parent->_parent;
    45. if (parent == grandfather->_left)
    46. {
    47. // 关键是看叔叔
    48. Node* uncle = grandfather->_right;
    49. // 情况1:uncle存在且为红
    50. if (uncle&&uncle->_col == RED)
    51. {
    52. parent->_col = uncle->_col = BLACK;
    53. grandfather->_col = RED;
    54. // 继续往上处理
    55. cur = grandfather;
    56. parent = cur->_parent;
    57. }
    58. else// 情况2+3:uncle不存在 uncle存在且为黑
    59. {
    60. // 情况2:单旋
    61. if (cur == parent->_left)
    62. {
    63. RotateR(grandfather);
    64. parent->_col = BLACK ;
    65. grandfather->_col = RED;
    66. }
    67. // 情况3:双旋
    68. else
    69. {
    70. RotateL(parent);
    71. RotateR(grandfather);
    72. cur->_col = BLACK;
    73. grandfather->_col = RED;
    74. }
    75. //最上面的节点已经变黑了,不用继续
    76. break;
    77. }
    78. }
    79. // 如果父亲存在,且颜色为红色就需要处理
    80. else
    81. {
    82. // 关键是看叔叔
    83. Node* uncle = grandfather->_left;
    84. // 情况1:uncle存在且为红
    85. if (uncle && uncle->_col == RED)
    86. {
    87. parent->_col = uncle->_col = BLACK;
    88. grandfather->_col = RED;
    89. // 继续往上处理
    90. cur = grandfather;
    91. parent = cur->_parent;
    92. }
    93. else // 情况2 + 3:uncle不存在 uncle存在且为黑
    94. {
    95. // 情况2:单旋
    96. if (cur == parent->_right)
    97. {
    98. RotateL(grandfather);
    99. parent->_col = BLACK;
    100. grandfather->_col = RED;
    101. }
    102. // 情况3:双旋
    103. else
    104. {
    105. RotateR(parent);
    106. RotateL(grandfather);
    107. cur->_col = BLACK;
    108. grandfather->_col = RED;
    109. }
    110. }
    111. break;
    112. }
    113. }
    114. _root->_col = BLACK;
    115. return make_pair(newnode, true);
    116. }

     插入整体逻辑:

    1. 如果还没有元素是一课空树,直接插入即可;如果有元素,按pair的first(key)和比较的节点比较结果为大说明为空的那个位置在右边,和比较的节点比较的结果小说明为空的哪个位置在左边;如果相等说明已经有这个元素了,二叉搜索树不支持冗余,插入失败则,返回一个pair类第一个成员为那个相同元素的map的迭代器和第二个成员为false的pair类迭代器;
    2. 不知道这个已经找到的位置在父节点的左边还是右边,需要判断一下,然后插入元素;
    3. 考虑变色

    3.1不平衡处理

    如果有父亲且父亲为红色说明不平衡,就一直向上调整,直到cur到头节点或者parent为黑色

    1.第一种情况:有uncle并且uncle为红色处理:parent和uncle变为黑色,grandfather变红色

    • 一直向上调整可能会让头节点变红,那么就在循环外把头节点处理一下

     2.第二种情况,没有uncle或者有uncle且为黑色,有uncle一定是第一种情况变化而来

    3.1parent在grandfather的左边

    这种情况单纯的变色已经做不到平衡了,怎么办?

    旋转处理:parent在grandfather的左边,右单旋和左右双旋

     3.1parent在grandfather的右边

    • 逻辑和在左边是一样的,大家可以自己尝试画一下
    • 旋转我在AVL树右详细解答http://t.csdn.cn/AlRzI

    旋转代码:

    1. void RotateL(Node* parent)
    2. {
    3. Node* subR = parent->_right;
    4. Node* subRL = subR->_left;
    5. parent->_right = subRL;
    6. if (subRL)
    7. {
    8. subRL->_parent = parent;
    9. }
    10. subR->_left = parent;
    11. Node* parentParent = parent->_parent;
    12. parent->_parent = subR;
    13. if (parent == _root)
    14. {
    15. _root = subR;
    16. _root->_parent = nullptr;
    17. }
    18. else
    19. {
    20. if (parentParent->_left == parent)
    21. {
    22. parentParent->_left = subR;
    23. }
    24. else
    25. {
    26. parentParent->_right = subR;
    27. }
    28. subR->_parent = parentParent;
    29. }
    30. }
    31. void RotateR(Node* parent)
    32. {
    33. Node* subL = parent->_left;
    34. Node* subLR = subL->_right;
    35. parent->_left = subLR;
    36. if (subLR)
    37. subLR->_parent = parent;
    38. subL->_right = parent;
    39. Node* parentParent = parent->_parent;
    40. parent->_parent = subL;
    41. if (parent == _root)
    42. {
    43. _root = subL;
    44. _root->_parent = nullptr;
    45. }
    46. else
    47. {
    48. if (parentParent->_left == parent)
    49. parentParent->_left = subL;
    50. else
    51. parentParent->_right = subL;
    52. subL->_parent = parentParent;
    53. }
    54. }

     4.测试自己的红黑树是不是平衡的

    • 测试了头节点是不是黑色,是否有连续的红节点,每条路径上的黑节点
    1. bool _CheckBalance(Node* root,int LeftNum,int count)
    2. {
    3. if (root == nullptr)
    4. {
    5. if (count != LeftNum)
    6. {
    7. return false;
    8. }
    9. return true;
    10. }
    11. if (root->_col == RED && root->_parent->_col == RED)
    12. {
    13. cout << "存在连续的红色节点" << endl;
    14. return false;
    15. }
    16. if (root->_col == BLACK)
    17. {
    18. count++;
    19. }
    20. return _CheckBalance(root->_left, LeftNum, count) &&
    21. _CheckBalance(root->_right, LeftNum, count);
    22. }
    23. bool CheckBalance()
    24. {
    25. if (_root == nullptr)
    26. {
    27. //空树是红黑树
    28. return true;
    29. }
    30. else if(_root->_col==RED)
    31. {
    32. cout << "根节点是红色的" << endl;
    33. return false;
    34. }
    35. else
    36. {
    37. int LeftNum = 0;
    38. Node* left = _root;
    39. // 找最左路径做黑色节点数量参考值
    40. while (left)
    41. {
    42. if (left->_col == BLACK)
    43. {
    44. LeftNum++;
    45. }
    46. left = left->_left;
    47. }
    48. int count = 0;
    49. return _CheckBalance(_root, LeftNum, count);
    50. }
    51. }

  • 相关阅读:
    python之字符串
    vue3 父组件与子组件v-model双向绑定
    Impala解决cast导致UDF ERROR: Decimal expression overflowed
    读书笔记-《ON JAVA 中文版》-摘要3
    位图和布隆过滤器
    金仓数据库KingbaseES备份与恢复工具手册(备份)
    用动态表动态生成DEV Gridview
    Java 线程详解
    Spring boot使用https协议
    傅里叶变换
  • 原文地址:https://blog.csdn.net/m0_72964546/article/details/127667639