当前位置:   article > 正文

c++代码手撕红黑树_c++ curfit

c++ curfit

企业里永远是技术驱动理论发展

比起理解红黑树的原理,更重要的是理解红黑树的应用场景,因为某些应用场景的需要,红黑树才会应运而生。

红黑树的特点:

插入,删除,查找都是O(logn)的复杂度。

红黑树的应用:

  • epoll的实现,内核会在内存开辟一个空间存放epoll的红黑树,并将每个epollfd加入到红黑树中,一般epoll会设置LT水平触发,当网卡有数据到来,可读缓冲区不为空,会触发回调EPOLLIN事件,而之前注册了对EPOLLIN事件感兴趣的socketfd会有专门的队列存储,内核会遍历队列搜寻对应的socketfd,因为在红黑树里找有近似O(logn)的时间复杂度,所以10亿个socket也只需要20次查找。

  • 进程调度,内核CFS队列,以红黑树的形式存储进程信息。

  • 有的hashtable(记得是java),当冲突时不以链表来组织重复元素,而是以红黑树的形式来组织。

  • 内存管理,比如空闲链表freelist可以通过红黑树来组织,这样malloc的时候要找到符合大小的内存块,如果不是firstfit的原则,而是全局最优大小原则,想找到适合的内存块就可以通过红黑树来找。

  • Nginx的Timer事件管理。

红黑树定义

写代码之前先写一下红黑树的规则吧

  1. 每个颜色不是红就是黑。

  2. 根节点必黑

  3. 叶子节点必黑

  4. 红色节点的左右孩子都为黑

  5. 每个节点到其叶子节点(nil)的所有路径上的黑色节点数量都一样

我的理解:从任一节点到叶子结点的路径上,路径的元素必然有黑色节点,而路径的长度则取决于路径上红色节点的数量,最短的路径上,所有节点都是黑色,这种情况下,查找效率为真实的O(logn),和严格平衡的AVL树一致。而如果在刚刚的最短路径上,也就是所有黑色节点的中间插入红色节点,这样是不会打破红黑树的平衡的(规则5),此时便是最长路径,查找效率为2logn,但依然和logn在同一数量级,因此,红黑树的查找效率可以看做是(Ologn),同时比AVL树拥有更高的插入和删除效率。

红黑树代码框架

  1. //-既然标题是c++,那么就写成满满的c++风格吧
  2. using Color = bool;//-颜色因为只有红或者黑,选择bool类型
  3. using KEY_TYPE = int;//-为了更好理解红黑树,就不写成模板类了,所以首选万年int(笑~)
  4. using VALUE_TYPE = int;//-同理
  5. //-全局静态红黑变量
  6. static const Color red = false;
  7. static const Color black = true;
  8. //-红黑树的节点特点,有color,有parent
  9. class RBtree_node{
  10. public:
  11. Color color;
  12. RBtree_node * parent;
  13. RBtree_node * left;
  14. RBtree_node * right;
  15. KEY_TYPE key;//-后期如果想解耦合,可以将keyvalue抽离出去
  16. VALUE_TYPE value;
  17. RBtree_node(Color color_):color(color_),parent(nullptr),left(nullptr),right(nullptr),key(-99999){}
  18. RBtree_node(Color color_, KEY_TYPE key_,RBtree_node * nil):
  19. color(color_),parent(nil),left(nil),right(nil),key(key_){}
  20. };
  21. class RBtree{
  22. private:
  23. //-红黑树数据成员:其中nil的意义在于,因为红黑树的所有叶子节点都是黑色的,所以可以将所有临近末尾的节点,
  24. //-都连接到这一个叶子结点nil上,同理,root的parent也可以连接到nil上,形成一个dummy空节点
  25. RBtree_node * root;
  26. RBtree_node * nil;
  27. public :
  28. //-以下实现了红黑树常用接口:
  29. //-构造函数
  30. RBtree(){
  31. nil = new RBtree_node(black);//-为所有叶子节点nil初始化,颜色为黑色
  32. root = nil;//-红黑树为空的时候,让nil作为root
  33. }
  34. //-左旋
  35. void leftRotate(RBtree_node *left_node);
  36. //- 右旋
  37. void rightRotate(RBtree_node * right_node);
  38. //-插入key
  39. void insertNode(KEY_TYPE key);
  40. //-修复插入
  41. void fixInsert(RBtree_node * node);
  42. //-查找某个key的节点
  43. RBtree_node* searchNode(KEY_TYPE key);
  44. //-查找某个节点的中序后继
  45. RBtree_node* successor(RBtree_node * node);
  46. //-删除key
  47. void deleteNode(KEY_TYPE key);
  48. //-修复删除
  49. void fixDelete(RBtree_node * node);
  50. //-层序遍历打印红黑树
  51. void print();
  52. //-打印中序遍历
  53. void printMiddle(RBtree_node * node);
  54. };

相关视频推荐

5种红黑树的用途,从应用到内核场景的优缺点

源码阅读:STL 红黑树、散列表的实现

c/c++后端开发需要学些什么?迭代13次的c/c++后端开发学习路线分享

免费学习地址:c/c++ linux服务器开发/后台架构师

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

接下来将接口一一实现:

红黑树节点左旋右旋:

实现参照该图,至于学习方法也没啥捷径,只能把这个结构图和变换方式深深印刻在脑海里。

手撕代码的时候想象一下还是容易写的,如果觉得这样很累就纸上画个草图。

由于左旋和右旋是对称的,所以规则只需要记一半。

​图1 左旋右旋

  1. //-左旋
  2. void RBtree::leftRotate(RBtree_node *left_node){
  3. RBtree_node * right_node = left_node->right;
  4. //-右节点的左枝条接在左节点的右枝条上
  5. left_node->right = right_node->left;
  6. if(right_node->left!=nil){
  7. left_node->right->parent = left_node;
  8. }
  9. //-右节点接在左节点的父亲上
  10. right_node->parent = left_node->parent;
  11. if(left_node == root){
  12. //-nil不会指向任何节点,但是root->parent是nil
  13. root = right_node;
  14. }
  15. else if(left_node == left_node->parent->left){
  16. left_node->parent->left = right_node;
  17. }else{
  18. left_node->parent->right = right_node;
  19. }
  20. //-左节点接在右节点的左枝上
  21. left_node->parent = right_node;
  22. right_node->left = left_node;
  23. }
  24. //- 右旋:写完左旋后,把所有leftright对调即可
  25. void RBtree::rightRotate(RBtree_node * right_node){
  26. RBtree_node * left_node = right_node->left;
  27. right_node->left = left_node->right;
  28. if(left_node->right!=nil){
  29. right_node->left->parent = right_node;
  30. }
  31. left_node->parent = right_node->parent;
  32. if(right_node == root){
  33. root = left_node;
  34. }
  35. else if(right_node == right_node->parent->right){
  36. right_node->parent->right = left_node;
  37. }else{
  38. right_node->parent->left = left_node;
  39. }
  40. right_node->parent = left_node;
  41. left_node->right = right_node;
  42. }

红黑树的插入,插入修复

插入的步骤原理:

  • 找到插入位置,注意红黑树新节点的插入位置都是叶子结点。

  • 如果红黑树中没有节点,插入节点需要改变root指向,同时将root的parent指向nil。

  • 改变插入节点父亲的左右指针,同时插入节点本身的左右指针指向nil。

  • 如果插入节点的父亲是红色,说明平衡被打破了,需要执行修复插入,让红黑树恢复平衡

要点:

  • 如果在查找的时候发现元素已经存在,我这里就直接抛弃了新元素的插入,如果要实现红黑树multimap的insert_equal功能可以自己实现一下。

  • 为什么要插入修复?

首先我们会强制默认所有的新节点都是红色节点。

因为红色节点不论插在哪个位置,都不会破坏规则5(路径上黑色节点数量相同),唯一可能破坏的是规则4(红色节点的孩子必黑),由于破坏规则5比破坏规则4要容易得多,所以将新节点设置为红色可以尽量地避免破坏规则。

当新的红色节点插入到一个红色节点之后,破坏了规则4,才需要修复,如图2,插入元素16

修复的意义和规则在于,如何将新红节点的父亲(34)变成黑色后,依然能保持红黑树的左右平衡,这个时候才涉及到对伯父节点(184)的讨论

插入修复的步骤原理:

  • 我们的目的是为了让左边cur为红的情况下,使父亲变黑且不会破坏平衡。

  • 所以只要cur的parent是红色,就一直循环。

  • 判断伯父节点(184),如果伯父节点是红色,如图2,那么同时将父亲和伯父改成黑色就不会改变平衡,将祖父(101)变红,让cur变成祖父(101)进入下一轮迭代。

​图2 伯父(184)为红

  • 如果伯父节点是黑色,如图3,插入节点16

​图3 伯父(184)为黑

  • 那么将父亲(34)变黑就会让左边多一个黑色,不过可以通过让祖父(101)变红,旋转祖父,让祖父下沉,父亲上浮,这样相当于让老爹变黑同时左右都加了一个黑色,不会破坏平衡。

  • 但是旋转祖父需要有个前提条件,插入节点不能是父亲的靠内节点,如图4,插入节点(36)为右孩子,父亲(34)是祖父(101)左枝。

​图4,插入节点(36)是靠内节点

  • 一旦右旋祖父(101),就会破坏cur(36)和父亲(34)的连接关系,所以必须要把cur从靠内节点变成靠外节,一个方便的方式是让父亲成为新cur(34),并右旋cur(34),如图5,之后再进行上个步骤,将新父亲(36)变黑,祖父(101)变红,右旋祖父。

​图5 原cur(36)经过旋转变为父亲,将34作为新cur

要点:

  • 终止条件,当当前节点(红)的父亲为黑的时候打破循环(注意回溯到nil的时候的,nil也是黑色)

  • 终止循环后,注意如果回溯到root,会改变root的颜色为红,需要在循环结束后fix成黑色。

  • 因为父亲是祖父左枝也好右枝也好,变换总是左右对称的,所以规则只需要记一半。

  1. //-插入key
  2. void RBtree::insertNode(KEY_TYPE key){
  3. RBtree_node * prev = nil;
  4. RBtree_node * cur = root;
  5. while(cur!=nil){
  6. prev = cur;
  7. if(key>cur->key){
  8. cur = cur->right;
  9. }else if(key<cur->key){
  10. cur = cur->left;
  11. }else{//-该key已经存在
  12. return;
  13. }
  14. }
  15. //-创建新节点
  16. RBtree_node * new_node = new RBtree_node(red,key,nil);
  17. //-如果节点没有元素
  18. new_node->parent = prev;
  19. if(prev == nil){
  20. root = new_node;
  21. }
  22. else if(key<prev -> key){
  23. prev ->left = new_node;
  24. }else{
  25. prev ->right = new_node;
  26. }
  27. fixInsert(new_node);
  28. print();
  29. }
  30. //-修复插入
  31. void RBtree::fixInsert(RBtree_node * new_node){
  32. while(new_node -> parent->color == red){//-终止条件要注意
  33. //-如果父亲是左枝
  34. if(new_node->parent == new_node -> parent->parent->left){
  35. //-获得其伯父节点
  36. RBtree_node * uncle = new_node->parent->parent->right;
  37. if(uncle->color == red){//-如果伯父是红色,那么将父亲和伯父同时变黑,不会破坏左右平衡
  38. uncle->color = black;
  39. new_node->parent->color = black;
  40. new_node->parent ->parent->color = red;//-将祖父变红,才能实现下一轮回溯修复
  41. new_node = new_node->parent->parent;
  42. }else{//-如果伯父是黑色
  43. //-判断new_node是不是右孩子,如果是右孩子转换成左孩子
  44. if(new_node == new_node -> parent->right){
  45. new_node = new_node->parent;
  46. leftRotate(new_node);
  47. }
  48. //-此时红色节点是左孩子
  49. //-如果结构本是平衡状态,右边本该比左边多一个黑,但是我们将父亲(左)变黑会破坏平衡,
  50. //-所以需要右旋祖父,把父亲上浮,相当于在左枝多一个黑的时候给右枝也多了黑,这样左右就能平衡
  51. new_node->parent->color = black;
  52. new_node->parent ->parent->color = red;
  53. rightRotate(new_node->parent->parent);
  54. }
  55. }
  56. //-如果父亲是右枝(将上边代码的leftright全部对调即可,不用记规则)
  57. else {
  58. RBtree_node * uncle = new_node->parent->parent->left;
  59. if(uncle->color == red){//-如果伯父是红色
  60. uncle->color = black;
  61. new_node->parent->color = black;
  62. new_node->parent ->parent->color = red;
  63. new_node = new_node->parent->parent;
  64. }else{//-如果伯父是黑色
  65. if(new_node == new_node -> parent->left){
  66. new_node = new_node->parent;
  67. rightRotate(new_node);
  68. }
  69. new_node->parent->color = black;
  70. new_node->parent ->parent->color = red;
  71. leftRotate(new_node->parent->parent);
  72. }
  73. }
  74. }
  75. //-如果new_node回溯到root,此时root->parent==nil(black)打破了循环,而此时root被改变成了黑色,违反了规则1
  76. //-所以最后需要强行把root fix成黑色
  77. root->color = black;
  78. }

红黑树查找某个key,以及找到某个节点的中序后继

主要讲下怎么根据当前节点找中序后继,根据BST的特性

  • 如果当前节点有右孩子:其后继肯定在右枝条上,且是右枝条最左边的元素。

  • 如果当前节点没有右孩子:根据中序遍历的递归顺序,假设cur是其父亲的左孩子,cur遍历完后,下一个节点(后继)就是父亲,反之,如果cur是右孩子,说明其父亲也递归完了,需要回溯父亲的父亲,所以只需要一直往上找直到cur为其parent的左孩子为止,然后返回parent,而回溯到root的时候,root的父亲虽然是nil,但是nil是没有左右孩子的,所以退出循环。

  1. //-查找某个key的节点
  2. RBtree_node* RBtree::searchNode(KEY_TYPE key){
  3. RBtree_node * cur = root;
  4. while(cur!=nil){
  5. if(key>cur -> key){
  6. cur = cur->right;
  7. }else if(key < cur -> key){
  8. cur = cur->left;
  9. }else{
  10. return cur;
  11. }
  12. }
  13. return cur;
  14. }
  15. //-查找某个节点的中序后继
  16. RBtree_node* RBtree::successor(RBtree_node * node){
  17. //-如果节点有右孩子
  18. if(node->right!=nil){
  19. RBtree_node * res = node -> right;
  20. while(res->left!=nil){
  21. res = res->left;
  22. }
  23. return res;
  24. }else{
  25. while(node!=root&&node!=node->parent->left){
  26. node = node->parent;
  27. }
  28. return node->parent;
  29. }
  30. }

红黑树删除,修复删除

删除的步骤原理:

  • 类似二叉堆,当我们从栈顶pop元素后,需要用二叉树末尾节点代替原来的root,而后从二叉堆顶部开始向下修复,同理,我们要删除树上的一个节点,自然需要一个节点来顶替删除节点(key_node)的位置,因次,我们并不一定要在数据结构上真正删除key_node,可以找到那个顶替节点(delete_node),将顶替节点的数据覆盖key_node,而在数据结构上真正删除的是那个顶替节点(delete_node)。

  • 在数据结构上真正删除哪个节点(delete_node怎么取),取决于(key_node)是否有左右枝条,

  • 如果key_node左右孩子都没有,说明是叶子节点,直接删除key_node即可,delete_node就是key_node本身。

  • 如果key_node左右孩子只有其一,那么删除key_node只需要将孩子接在祖父上,删除自己即可,所以delete_node依旧是key_node本身。

  • 如果key_node左右孩子都有,那么可以根据上面的successor函数,找到key_node的直接后继,也就是删除节点右边最小的元素,将其本身数据用来顶替key_node,不会破坏BST的性质。之后将其作为delete_node在数据结构上删除即可,如图6。

图6

  • 找到delete_node后,还要找到delete_node的孩子(delete_son),将delete_son接在delete_node的父亲上。

  • 判断delete_node是否是黑色,如果是黑色,则删除了该元素必会破坏红黑树的平衡(规则5),需要修复fix_delete,而修复从delete_son开始。

要点:

  • 记得额外判断如果delete_node是root的情况,需要更新其孩子delete_son为新的root。

修复删除的步骤原理:

  • 删除节点delete_node如果是黑色的,说明树中有一个枝条(假设是左枝)的黑色节点必会比兄弟枝条(右枝)少一个。我们怎么才能使左右重新平衡,要么让左枝条黑色节点+1,要么让右枝条黑色节点-1。后续步骤全都以delete_son为其父左枝条为例,因为对称,依旧只需要记一半的规则。

  • 让delete_son的兄弟bro变成黑色,如果bro是红色,则bro->黑色,parent->红色,左旋parent,此时bro的左枝会变成新的bro,因为bro是红色,所以根据规则4,左枝必为黑,即新bro变为黑色。

  • 判断bro的孩子,

  • 如果左黑右黑,将bro->红色,不会改变bro后续孩子的平衡,同时,bro所在的右枝条的黑色节点-1,红黑树重新平衡,将父亲作为新的delete_son继续循环,直到delete_son为红色。

  • 如果左红右黑,通过右旋bro,变成左黑右红。

  • 如果左黑右红。bro继承父亲的颜色,将bro的父亲变黑,右孩子变黑(右枝黑+1),左旋父亲(左枝黑+1,右枝黑-1),总的来说delete_son所在的左枝条的黑色节点+1,红黑树重新平衡,并且直接让delete_son=root退出循环。

要点:

  • 终止条件:当delete_son==root或者delete_son为红的时候终止循环。

  1. //-删除key
  2. void RBtree::deleteNode(KEY_TYPE key){
  3. //-查找key所在节点
  4. RBtree_node * key_node = searchNode(key);
  5. //-实际删除的节点
  6. RBtree_node* delete_node;
  7. //-delete_node的孩子
  8. RBtree_node* delete_son;
  9. //-如果同时有左枝或者右枝条
  10. if(key_node->left != nil&&key_node->right != nil){
  11. delete_node = successor(key_node);
  12. delete_son = delete_node->right;
  13. }//-如果仅有左枝或者右枝条或者左右都没有
  14. else{
  15. delete_node = key_node;
  16. if(key_node->left != nil){
  17. delete_son = key_node->left;
  18. }else{
  19. delete_son = key_node->right;
  20. }
  21. }
  22. //-删除deletenode
  23. delete_son->parent = delete_node->parent;
  24. //-先判断deletenode是不是根节点
  25. if(delete_node == root){
  26. root = delete_son;
  27. }
  28. else if(delete_node == delete_node->parent->left){
  29. delete_node->parent->left = delete_son;
  30. }else{
  31. delete_node -> parent -> right = delete_son;
  32. }
  33. //-覆盖key_node原有数据
  34. key_node->key = delete_node -> key;
  35. key_node ->value = delete_node -> value;
  36. //-如果删除节点是黑色的,需要修复delete_son,注意是孩子
  37. if(delete_node->color == black){
  38. fixDelete(delete_son);
  39. }
  40. //-释放空间
  41. delete delete_node;
  42. //-打印
  43. print();
  44. }
  45. //-修复删除
  46. void RBtree::fixDelete(RBtree_node * delete_son){
  47. //-修复的原因是因为delete_son所在的枝条的黑节点比另一个枝条少一个,所以不平衡,所以需要填上左边缺失的黑,或者减掉右边多余的黑
  48. //-当delete_son是黑色的一直循环
  49. while(delete_son!=root&&delete_son->color == black){
  50. //-判断delete_son所在枝条,如果是左枝
  51. if(delete_son == delete_son->parent->left){
  52. //-如果兄弟是红色的
  53. RBtree_node * bro = delete_son->parent->right;
  54. if(bro->color == red){
  55. bro->color = black;//-兄弟变黑
  56. delete_son->parent->color = red;//-父亲变红
  57. leftRotate(delete_son->parent);//-左旋父亲,兄弟上浮,相当于左右都加了一个黑,不改变平衡状态
  58. bro = delete_son->parent->right;//-新的bro是原来bro的左枝,因为原bro是红的,其左右枝都是黑色的,这样保证新的兄弟是黑色的
  59. }
  60. //-此时兄弟是黑色的,判断兄弟的孩子
  61. //-左黑右黑(兄弟的孩子平衡了)
  62. if(bro->left->color == black&&bro->right -> color == black){
  63. bro->color = red;//-相当于右边减去多的一个黑,达到平衡
  64. delete_son = delete_son->parent;
  65. }else{
  66. //-如果是左红右黑,变成左黑右红
  67. if (bro->right->color == black){
  68. bro -> color = red;
  69. bro->left->color = black;
  70. rightRotate(bro);//-左节点上浮,相当于左右都加了一个黑,不改变平衡
  71. }
  72. bro->color = bro->parent -> color;
  73. bro->parent -> color = black;
  74. bro->right->color = black;//-给右边加了一个黑
  75. leftRotate(delete_son->parent);//-父亲下沉,兄弟上浮,左边加一个黑,右边减一个黑,总体上左边填上了缺少的黑也达到了平衡
  76. delete_son = root;
  77. }
  78. }
  79. //-如果是右枝(不用记规则,把上面的代码leftright对调即可)
  80. else {
  81. RBtree_node * bro = delete_son->parent->left;
  82. if(bro->color == red){
  83. bro->color = black;
  84. delete_son->parent->color = red;
  85. rightRotate(delete_son->parent);
  86. bro = delete_son->parent->left;
  87. }
  88. if(bro->right->color == black&&bro->left -> color == black){
  89. bro->color = red;
  90. delete_son = delete_son->parent;
  91. }else{
  92. if (bro->left->color == black){
  93. bro -> color = red;
  94. bro->right->color = black;
  95. leftRotate(bro);
  96. }
  97. bro->color = bro->parent -> color;
  98. bro->parent -> color = black;
  99. bro->left->color = black;
  100. rightRotate(delete_son->parent);
  101. delete_son = root;
  102. }
  103. }
  104. }
  105. delete_son->color = black;
  106. }

二叉树层序遍历和中序遍历

每次插入删除的时候,使用层序遍历打印一遍二叉树,可以验证一下是否正确。

每个节点都有前缀,b代表黑节点,r代表红节点。

  1. //-层序遍历打印红黑树
  2. void RBtree::print(){
  3. std::deque<RBtree_node*> dqueue;//-使用deque实现队列
  4. dqueue.push_back(root);
  5. while(!dqueue.empty()){
  6. int size = (int)dqueue.size();
  7. for (int i = 0; i < size; ++i) {
  8. RBtree_node* temp = dqueue.front();
  9. dqueue.pop_front();
  10. if(temp->left!=nullptr){
  11. dqueue.push_back(temp -> left);
  12. }
  13. if(temp -> right != nullptr){
  14. dqueue.push_back(temp -> right);
  15. }
  16. std::string color = temp->color?"b: ":"r: ";
  17. std::string keystr = temp==nil?"nil":std::to_string(temp->key);
  18. std::cout<<color<<keystr<<" ";
  19. }
  20. std::cout<<std::endl;
  21. }
  22. }
  23. //-打印中序遍历
  24. void RBtree::printMiddle(RBtree_node * node){
  25. if(node == nil){
  26. return;
  27. }
  28. printMiddle(node->left);
  29. std::string color = node->color?"b:":"r:";
  30. std::cout<<color<<std::to_string(node->key)<<" ";
  31. printMiddle(node->right);
  32. }

红黑树测试代码

写个循环来插入元素,输入i插入元素,输入d删除元素,输入q退出程序。

附上一个在线生成红黑树的连接,可以配合测试自己写的红黑树的正确性

红黑树动画在线演示

  1. int main(){
  2. RBtree rb;
  3. std::string select;
  4. KEY_TYPE key;
  5. while(true){
  6. std::cout<<"\n输入操作:i:插入key,d:删除key q:退出"<<std::endl;
  7. std::cin>>select;
  8. if(select == "i"){
  9. std::cout<<"输入key"<<std::endl;
  10. std::cin>>key;
  11. rb.insertNode(key);
  12. }else if(select == "d"){
  13. std::cout<<"输入key"<<std::endl;
  14. std::cin>>key;
  15. rb.deleteNode(key);
  16. }else if(select == "q"){
  17. break;
  18. }else{
  19. std::cout<<"输入不合法,重新输入"<<std::endl;
  20. }
  21. }
  22. return 0;
  23. }

完整代码

  1. #include <iostream>
  2. #include <deque>
  3. #include <string>
  4. #include <vector>
  5. //-既然标题是c++,那么就写成满满的c++风格吧
  6. using Color = bool;//-颜色因为只有红或者黑,选择bool类型
  7. using KEY_TYPE = int;//-为了更好理解红黑树,就不写成模板类了,所以首选万年int(笑~)
  8. using VALUE_TYPE = int;//-同理
  9. //-全局静态红黑变量
  10. static const Color red = false;
  11. static const Color black = true;
  12. //-红黑树的节点特点,有color,有parent
  13. class RBtree_node{
  14. public:
  15. Color color;
  16. RBtree_node * parent;
  17. RBtree_node * left;
  18. RBtree_node * right;
  19. KEY_TYPE key;//-后期如果想解耦合,可以将keyvalue抽离出去
  20. VALUE_TYPE value;
  21. RBtree_node(Color color_):color(color_),parent(nullptr),left(nullptr),right(nullptr),key(-99999){}
  22. RBtree_node(Color color_, KEY_TYPE key_,RBtree_node * nil):
  23. color(color_),parent(nil),left(nil),right(nil),key(key_){}
  24. };
  25. class RBtree{
  26. private:
  27. //-红黑树数据成员:其中nil的意义在于,因为红黑树的所有叶子节点都是黑色的,所以可以将所有临近末尾的节点,
  28. //-都连接到这一个叶子结点nil上,同理,root的parent也可以连接到nil上,形成一个dummy空节点
  29. RBtree_node * root;
  30. RBtree_node * nil;
  31. public :
  32. //-以下实现了红黑树常用接口:
  33. //-构造函数
  34. RBtree(){
  35. nil = new RBtree_node(black);//-为所有叶子节点nil初始化,颜色为黑色
  36. root = nil;//-红黑树为空的时候,让nil作为root
  37. }
  38. //-左旋
  39. void leftRotate(RBtree_node *left_node);
  40. //- 右旋
  41. void rightRotate(RBtree_node * right_node);
  42. //-插入key
  43. void insertNode(KEY_TYPE key);
  44. //-修复插入
  45. void fixInsert(RBtree_node * node);
  46. //-查找某个key的节点
  47. RBtree_node* searchNode(KEY_TYPE key);
  48. //-查找某个节点的中序后继
  49. RBtree_node* successor(RBtree_node * node);
  50. //-删除key
  51. void deleteNode(KEY_TYPE key);
  52. //-修复删除
  53. void fixDelete(RBtree_node * node);
  54. //-层序遍历打印红黑树
  55. void print();
  56. //-打印中序遍历
  57. void printMiddle(RBtree_node * node);
  58. };
  59. //-左旋
  60. void RBtree::leftRotate(RBtree_node *left_node){
  61. RBtree_node * right_node = left_node->right;
  62. //-右节点的左枝条接在左节点的右枝条上
  63. left_node->right = right_node->left;
  64. if(right_node->left!=nil){
  65. left_node->right->parent = left_node;
  66. }
  67. //-右节点接在左节点的父亲上
  68. right_node->parent = left_node->parent;
  69. if(left_node == root){
  70. //-nil不会指向任何节点,但是root->parent是nil
  71. root = right_node;
  72. }
  73. else if(left_node == left_node->parent->left){
  74. left_node->parent->left = right_node;
  75. }else{
  76. left_node->parent->right = right_node;
  77. }
  78. //-左节点接在右节点的左枝上
  79. left_node->parent = right_node;
  80. right_node->left = left_node;
  81. }
  82. //- 右旋:写完左旋后,把所有leftright对调即可
  83. void RBtree::rightRotate(RBtree_node * right_node){
  84. RBtree_node * left_node = right_node->left;
  85. right_node->left = left_node->right;
  86. if(left_node->right!=nil){
  87. right_node->left->parent = right_node;
  88. }
  89. left_node->parent = right_node->parent;
  90. if(right_node == root){
  91. root = left_node;
  92. }
  93. else if(right_node == right_node->parent->right){
  94. right_node->parent->right = left_node;
  95. }else{
  96. right_node->parent->left = left_node;
  97. }
  98. right_node->parent = left_node;
  99. left_node->right = right_node;
  100. }
  101. //-插入key
  102. void RBtree::insertNode(KEY_TYPE key){
  103. RBtree_node * prev = nil;
  104. RBtree_node * cur = root;
  105. while(cur!=nil){
  106. prev = cur;
  107. if(key>cur->key){
  108. cur = cur->right;
  109. }else if(key<cur->key){
  110. cur = cur->left;
  111. }else{//-该key已经存在
  112. return;
  113. }
  114. }
  115. //-创建新节点
  116. RBtree_node * new_node = new RBtree_node(red,key,nil);
  117. //-如果节点没有元素
  118. new_node->parent = prev;
  119. if(prev == nil){
  120. root = new_node;
  121. }
  122. else if(key<prev -> key){
  123. prev ->left = new_node;
  124. }else{
  125. prev ->right = new_node;
  126. }
  127. fixInsert(new_node);
  128. print();
  129. }
  130. //-修复插入
  131. void RBtree::fixInsert(RBtree_node * new_node){
  132. while(new_node -> parent->color == red){//-终止条件要注意
  133. //-如果父亲是左枝
  134. if(new_node->parent == new_node -> parent->parent->left){
  135. //-获得其伯父节点
  136. RBtree_node * uncle = new_node->parent->parent->right;
  137. if(uncle->color == red){//-如果伯父是红色,那么将父亲和伯父同时变黑,不会破坏左右平衡
  138. uncle->color = black;
  139. new_node->parent->color = black;
  140. new_node->parent ->parent->color = red;//-将祖父变红,才能实现下一轮回溯修复
  141. new_node = new_node->parent->parent;
  142. }else{//-如果伯父是黑色
  143. //-判断new_node是不是右孩子,如果是右孩子转换成左孩子
  144. if(new_node == new_node -> parent->right){
  145. new_node = new_node->parent;
  146. leftRotate(new_node);
  147. }
  148. //-此时红色节点是左孩子
  149. //-如果结构本是平衡状态,右边本该比左边多一个黑,但是我们将父亲(左)变黑会破坏平衡,
  150. //-所以需要右旋祖父,把父亲上浮,相当于在左枝多一个黑的时候给右枝也多了黑,这样左右就能平衡
  151. new_node->parent->color = black;
  152. new_node->parent ->parent->color = red;
  153. rightRotate(new_node->parent->parent);
  154. }
  155. }
  156. //-如果父亲是右枝(将上边代码的leftright全部对调即可,不用记规则)
  157. else {
  158. RBtree_node * uncle = new_node->parent->parent->left;
  159. if(uncle->color == red){//-如果伯父是红色
  160. uncle->color = black;
  161. new_node->parent->color = black;
  162. new_node->parent ->parent->color = red;
  163. new_node = new_node->parent->parent;
  164. }else{//-如果伯父是黑色
  165. if(new_node == new_node -> parent->left){
  166. new_node = new_node->parent;
  167. rightRotate(new_node);
  168. }
  169. new_node->parent->color = black;
  170. new_node->parent ->parent->color = red;
  171. leftRotate(new_node->parent->parent);
  172. }
  173. }
  174. }
  175. //-如果new_node回溯到root,此时root->parent==nil(black)打破了循环,而此时root被改变成了黑色,违反了规则1
  176. //-所以最后需要强行把root fix成黑色
  177. root->color = black;
  178. }
  179. //-查找某个key的节点
  180. RBtree_node* RBtree::searchNode(KEY_TYPE key){
  181. RBtree_node * cur = root;
  182. while(cur!=nil){
  183. if(key>cur -> key){
  184. cur = cur->right;
  185. }else if(key < cur -> key){
  186. cur = cur->left;
  187. }else{
  188. return cur;
  189. }
  190. }
  191. return cur;
  192. }
  193. //-查找某个节点的中序后继
  194. RBtree_node* RBtree::successor(RBtree_node * node){
  195. //-如果节点有右孩子
  196. if(node->right!=nil){
  197. RBtree_node * res = node -> right;
  198. while(res->left!=nil){
  199. res = res->left;
  200. }
  201. return res;
  202. }else{
  203. while(node!=root&&node!=node->parent->left){
  204. node = node->parent;
  205. }
  206. return node->parent;
  207. }
  208. }
  209. //-删除key
  210. void RBtree::deleteNode(KEY_TYPE key){
  211. //-查找key所在节点
  212. RBtree_node * key_node = searchNode(key);
  213. //-实际删除的节点
  214. RBtree_node* delete_node;
  215. //-delete_node的孩子
  216. RBtree_node* delete_son;
  217. //-如果同时有左枝或者右枝条
  218. if(key_node->left != nil&&key_node->right != nil){
  219. delete_node = successor(key_node);
  220. delete_son = delete_node->right;
  221. }//-如果仅有左枝或者右枝条或者左右都没有
  222. else{
  223. delete_node = key_node;
  224. if(key_node->left != nil){
  225. delete_son = key_node->left;
  226. }else{
  227. delete_son = key_node->right;
  228. }
  229. }
  230. //-删除deletenode
  231. delete_son->parent = delete_node->parent;
  232. //-先判断deletenode是不是根节点
  233. if(delete_node == root){
  234. root = delete_son;
  235. }
  236. else if(delete_node == delete_node->parent->left){
  237. delete_node->parent->left = delete_son;
  238. }else{
  239. delete_node -> parent -> right = delete_son;
  240. }
  241. //-覆盖key_node原有数据
  242. key_node->key = delete_node -> key;
  243. key_node ->value = delete_node -> value;
  244. //-如果删除节点是黑色的,需要修复delete_son,注意是孩子
  245. if(delete_node->color == black){
  246. fixDelete(delete_son);
  247. }
  248. //-释放空间
  249. delete delete_node;
  250. //-打印
  251. print();
  252. }
  253. //-修复删除
  254. void RBtree::fixDelete(RBtree_node * delete_son){
  255. //-修复的原因是因为delete_son所在的枝条的黑节点比另一个枝条少一个,所以不平衡,所以需要填上左边缺失的黑,或者减掉右边多余的黑
  256. //-当delete_son是黑色的一直循环
  257. while(delete_son!=root&&delete_son->color == black){
  258. //-判断delete_son所在枝条,如果是左枝
  259. if(delete_son == delete_son->parent->left){
  260. //-如果兄弟是红色的
  261. RBtree_node * bro = delete_son->parent->right;
  262. if(bro->color == red){
  263. bro->color = black;//-兄弟变黑
  264. delete_son->parent->color = red;//-父亲变红
  265. leftRotate(delete_son->parent);//-左旋父亲,兄弟上浮,相当于左右都加了一个黑,不改变平衡状态
  266. bro = delete_son->parent->right;//-新的bro是原来bro的左枝,因为原bro是红的,其左右枝都是黑色的,这样保证新的兄弟是黑色的
  267. }
  268. //-此时兄弟是黑色的,判断兄弟的孩子
  269. //-左黑右黑(兄弟的孩子平衡了)
  270. if(bro->left->color == black&&bro->right -> color == black){
  271. bro->color = red;//-相当于右边减去多的一个黑,达到平衡
  272. delete_son = delete_son->parent;
  273. }else{
  274. //-如果是左红右黑,变成左黑右红
  275. if (bro->right->color == black){
  276. bro -> color = red;
  277. bro->left->color = black;
  278. rightRotate(bro);//-左节点上浮,相当于左右都加了一个黑,不改变平衡
  279. }
  280. bro->color = bro->parent -> color;
  281. bro->parent -> color = black;
  282. bro->right->color = black;//-给右边加了一个黑
  283. leftRotate(delete_son->parent);//-父亲下沉,兄弟上浮,左边加一个黑,右边减一个黑,总体上左边填上了缺少的黑也达到了平衡
  284. delete_son = root;
  285. }
  286. }
  287. //-如果是右枝(不用记规则,把上面的代码leftright对调即可)
  288. else {
  289. RBtree_node * bro = delete_son->parent->left;
  290. if(bro->color == red){
  291. bro->color = black;
  292. delete_son->parent->color = red;
  293. rightRotate(delete_son->parent);
  294. bro = delete_son->parent->left;
  295. }
  296. if(bro->right->color == black&&bro->left -> color == black){
  297. bro->color = red;
  298. delete_son = delete_son->parent;
  299. }else{
  300. if (bro->left->color == black){
  301. bro -> color = red;
  302. bro->right->color = black;
  303. leftRotate(bro);
  304. }
  305. bro->color = bro->parent -> color;
  306. bro->parent -> color = black;
  307. bro->left->color = black;
  308. rightRotate(delete_son->parent);
  309. delete_son = root;
  310. }
  311. }
  312. }
  313. delete_son->color = black;
  314. }
  315. //-层序遍历打印红黑树
  316. void RBtree::print(){
  317. std::deque<RBtree_node*> dqueue;//-使用deque实现队列
  318. dqueue.push_back(root);
  319. while(!dqueue.empty()){
  320. int size = (int)dqueue.size();
  321. for (int i = 0; i < size; ++i) {
  322. RBtree_node* temp = dqueue.front();
  323. dqueue.pop_front();
  324. if(temp->left!=nullptr){
  325. dqueue.push_back(temp -> left);
  326. }
  327. if(temp -> right != nullptr){
  328. dqueue.push_back(temp -> right);
  329. }
  330. std::string color = temp->color?"b: ":"r: ";
  331. std::string keystr = temp==nil?"nil":std::to_string(temp->key);
  332. std::cout<<color<<keystr<<" ";
  333. }
  334. std::cout<<std::endl;
  335. }
  336. }
  337. //-打印中序遍历
  338. void RBtree::printMiddle(RBtree_node * node){
  339. if(node == nil){
  340. return;
  341. }
  342. printMiddle(node->left);
  343. std::string color = node->color?"b:":"r:";
  344. std::cout<<color<<std::to_string(node->key)<<" ";
  345. printMiddle(node->right);
  346. }
  347. int main(){
  348. RBtree rb;
  349. std::string select;
  350. KEY_TYPE key;
  351. while(true){
  352. std::cout<<"\n输入操作:i:插入key,d:删除key q:退出"<<std::endl;
  353. std::cin>>select;
  354. if(select == "i"){
  355. std::cout<<"输入key"<<std::endl;
  356. std::cin>>key;
  357. rb.insertNode(key);
  358. }else if(select == "d"){
  359. std::cout<<"输入key"<<std::endl;
  360. std::cin>>key;
  361. rb.deleteNode(key);
  362. }else if(select == "q"){
  363. break;
  364. }else{
  365. std::cout<<"输入不合法,重新输入"<<std::endl;
  366. }
  367. }
  368. return 0;
  369. }

                
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/246040
推荐阅读
相关标签
  

闽ICP备14008679号