红黑树C++源码_学习笔记

7 篇文章 0 订阅

声明

需要结合wiki中的讲解看,此源码是对于wiki中源码的修正以及加注释,从而方便理解

wiki链接: 红黑树wiki

代码以及注释

/*
具体见:
https://zh.wikipedia.org/wiki/%E7%BA%A2%E9%BB%91%E6%A0%91

感觉源码的插入有问题,自己改了一下
然后删除中情形二的代码也有问题,wiki源码自己注释反了___版本匹配错了
// ```cpp
if(p == p->parent->leftTree)
    //rotate_left(p->sibling());
    rotate_left(p->parent);
// ```
应该要这样
// ```cpp
if(p == p->parent->leftTree)
    rotate_left(p->sibling());
    //rotate_left(p->parent);
// ```

************************
这个版本的左右旋源码核心是使用的那个节点的值是3个点值中的中间大小
************************


然后就是在删除那里卡死了,去看 STL源码剖析 ,发现那本书直接没有讲删除

然后找到一篇知乎文章,讲得比较妙的 B ... 让我瞬间懂了为啥要delete_case
下面我们开始讨论修复操作(下面的叶子节点都是指非NULL的叶子节点):

A. 删除的是叶子节点且该叶子节点是红色的 ---> 无需修复,因为它不会破坏红黑树的5个特性

B. 删除的是叶子节点且该叶子节点是黑色的 ---> 很明显会破坏特性5,需要修复。 ___ 对于其他路的影响

C. 删除的节点(为了便于叙述我们将其称为P)下面有一个子节点 S,对于这种情况我们通过 将P和S的值交换的方式,
   巧妙的将删除P变为删除S,S是叶子节点,这样C这种情况就会转 换为A, B这两种情况:

C1: P为黑色,S为红色 ---> 对应 A 这种情况

C2: P为黑色或红色,S为黑色 --- > 对应 B 这种情况

D. 删除的节点有两个子节点,对于这种情况,我们通过将P和它的后继节点N的值交换的方式,
   将删除节点P转换为删除后继节点N,而后继节点只可能是以下两种情况:

D1: N是叶子节点 --- > 对应情况 A 或 B

D2: N有一个子节点 ---- > 对应情况 C

所以通过上面的分析我们发现,红黑树节点删除后的修复操作都可以转换为 A 或 B这两种情况,而A不需要修复,
所以我们只需要研究B这种情况如何修复就行了。

*/

#define BLACK 1
#define RED 0
#include <iostream>

using namespace std;

class bst {
private:

    struct Node {
        int value;
        bool color;
        Node *leftTree, *rightTree, *parent;

        Node() : value(0), color(RED), leftTree(NULL), rightTree(NULL), parent(NULL) { }

        Node* grandparent() {
            if(parent == NULL){
                return NULL;
            }
            return parent->parent;
        }

        Node* uncle() {
            if(grandparent() == NULL) {
                return NULL;
            }
            if(parent == grandparent()->rightTree)
                return grandparent()->leftTree;
            else
                return grandparent()->rightTree;
        }

        Node* sibling() {
            if(parent->leftTree == this)
                return parent->rightTree;
            else
                return parent->leftTree;
        }
    };

    /*
    左右旋核心,掌握5个点的父子关系,分别是y,p,fa,root,gp
    先是处理y和fa的父子关系
    然后再是p和fa的父子关系
    再是p和root的关系
    最后是gp和p的父子关系
    */
    // 这里的p其实是"右旋"解析图中的 P
    // 这个版本的右旋是p作为左儿子的右旋
    // 三点中的中值
    /*
            G(maybe NULL)
          /
        P
      /
    N
    旋转后:
        P
      /   \
    N       G
    */
    void rotate_right(Node *p){
        Node *gp = p->grandparent();
        Node *fa = p->parent;
        Node *y = p->rightTree;

        fa->leftTree = y;

        if(y != NIL)
            y->parent = fa;
        p->rightTree = fa;
        fa->parent = p;

        if(root == fa)
            root = p;
        p->parent = gp;

        if(gp != NULL){
            if(gp->leftTree == fa)
                gp->leftTree = p;
            else
                gp->rightTree = p;
        }

    }

    // 这里的p其实是"左旋"解析图中的 N
    // 这个版本的左旋是p作为父节点的右儿子的左旋
    // 三点中的中值
    /*
        G(maybe NULL)
      /
    P
     \
      N
    转后:
        G
      /
    N
  /
P

    */
    void rotate_left(Node *p){
        if(p->parent == NULL){
            root = p;
            return;
        }
        // gp可能为nullptr的
        // 一开始就把很多节点都存起来了,所以超级安全
        Node *gp = p->grandparent();
        Node *fa = p->parent;
        Node *y = p->leftTree;

        fa->rightTree = y;

        if(y != NIL)
            y->parent = fa;
        p->leftTree = fa;
        fa->parent = p;

        if(root == fa)
            root = p;
        p->parent = gp;

        if(gp != NULL){
            if(gp->leftTree == fa)
                gp->leftTree = p;
            else
                gp->rightTree = p;
        }
    }

    void inorder(Node *p){
        if(p == NIL)
            return;

        if(p->leftTree)
            inorder(p->leftTree);

        cout << p->value << " ";

        if(p->rightTree)
            inorder(p->rightTree);
    }

    string outputColor (bool color) {
        return color ? "BLACK" : "RED";
    }

    Node* getSmallestChild(Node *p){
        if(p->leftTree == NIL)
            return p;
        return getSmallestChild(p->leftTree);
    }

    bool delete_child(Node *p, int data){
        if(p->value > data){
            if(p->leftTree == NIL){
                return false;
            }
            return delete_child(p->leftTree, data);
        } else if(p->value < data){
            if(p->rightTree == NIL){
                return false;
            }
            return delete_child(p->rightTree, data);
        } else if(p->value == data){
            if(p->rightTree == NIL){
                // 右孩子为NIL,就在左子树上做文章
                delete_one_child (p);
                return true;
            }
            // 右孩子不为NIL,就获取右子树中最小的那个节点
            // 那个节点要么左孩子为NIL,要么两边都为NIL
            // 综合前面的情况可以知道,两个孩子都可能为NIL or 其中的一个
            Node *smallest = getSmallestChild(p->rightTree);
            swap(p->value, smallest->value);
            delete_one_child (smallest);

            return true;
        }else{
            return false;
        }
    }

    void delete_one_child(Node *p){
        Node *child = p->leftTree == NIL ? p->rightTree : p->leftTree;
        if(p->parent == NULL && p->leftTree == NIL && p->rightTree == NIL){
            p = NULL;
            root = p;
            return;
        }

        if(p->parent == NULL){
            delete  p;
            child->parent = NULL;
            root = child;
            root->color = BLACK;
            return;
        }

        if(p->parent->leftTree == p){
            p->parent->leftTree = child;
        } else {
            p->parent->rightTree = child;
        }
        child->parent = p->parent;

        if(p->color == BLACK){
            if(child->color == RED){
                child->color = BLACK;
            } else
            // 儿子和被删除的父亲节点都是黑色
            // 两个孩子都必须为叶子
            // 那还要这个delete_case干嘛??
            // 卡了几个小时
            // 傻逼了,所以就是这一条路的黑色节点-1!!!会对其他路影响

            // 原来要删除的p被删除了,用它两个NIL叶子中的一个顶替了它的位置!
            // 所以之后讨论它的兄弟,其实是原来要删除的p的兄弟
            // NIL顶替位置
                delete_case (child);
        }

        delete p;
    }

    void delete_case(Node *p){
        if(p->parent == NULL){
            p->color = BLACK;
            return;
        }
        // 情形二
        // 原来的位置的兄弟为RED,则兄弟存在,不为NIL
        if(p->sibling()->color == RED) {
            p->parent->color = RED;
            p->sibling()->color = BLACK;
            if(p == p->parent->leftTree)
                rotate_left(p->sibling());
                // rotate_left(p->parent);
            else
                //rotate_right(p->sibling());
                rotate_right(p->parent);
        }
        // 情形3
        if(p->parent->color == BLACK && p->sibling()->color == BLACK
                && p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == BLACK) {
            p->sibling()->color = RED;
            delete_case(p->parent);
        } else if(p->parent->color == RED && p->sibling()->color == BLACK
                && p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == BLACK) {
            // 情形4
            p->sibling()->color = RED;
            p->parent->color = BLACK;
        } else {
            if(p->sibling()->color == BLACK) {
                // 情形5__算是一个中间处理
                if(p == p->parent->leftTree && p->sibling()->leftTree->color == RED
                        && p->sibling()->rightTree->color == BLACK) {
                    p->sibling()->color = RED;
                    p->sibling()->leftTree->color = BLACK;
                    rotate_right(p->sibling()->leftTree);
                } else if(p == p->parent->rightTree && p->sibling()->leftTree->color == BLACK
                        && p->sibling()->rightTree->color == RED) {
                    p->sibling()->color = RED;
                    p->sibling()->rightTree->color = BLACK;
                    rotate_left(p->sibling()->rightTree);
                }
            }
            // 情形六
            // 下面两行的颜色操作根据wiki来说是 没有意义的,
            // 本来就是相同的黑色__因为兄弟颜色是红的在 情形二 就处理过了
            // 而且父亲是红也在情形4和5处理过了__不对__情形4 和5 没处理完
            // 见图片( 文末 ) ___ wiki中没讲的情形7 _代码兼顾了
            p->sibling()->color = p->parent->color;
            p->parent->color = BLACK;
            if(p == p->parent->leftTree){
                p->sibling()->rightTree->color = BLACK;
                rotate_left(p->sibling());
            } else {
                p->sibling()->leftTree->color = BLACK;
                rotate_right(p->sibling());
            }
        }
    }

    void insert(Node *p, int data){
        if(p->value >= data){
            if(p->leftTree != NIL)
                insert(p->leftTree, data);
            else {
                Node *tmp = new Node();
                tmp->value = data;
                tmp->leftTree = tmp->rightTree = NIL;
                tmp->parent = p;
                p->leftTree = tmp;
                insert_case (tmp);
            }
        } else {
            if(p->rightTree != NIL)
                insert(p->rightTree, data);
            else {
                Node *tmp = new Node();
                tmp->value = data;
                tmp->leftTree = tmp->rightTree = NIL;
                tmp->parent = p;
                p->rightTree = tmp;
                insert_case (tmp);
            }
        }
    }

    void insert_case(Node *p){
        /*
        情形1:新节点N位于树的根上,没有父节点。
        在这种情形下,我们把它重绘为黑色以满足性质2。
        因为它在每个路径上对黑节点数目增加一,性质5符合。
        */
        if(p->parent == NULL){
            root = p;
            p->color = BLACK;
            return;
        }
        /*
        情形2(其实是下面if的else,不用写):新节点的父节点P是黑色,所以性质4没有失效
        (新节点是红色的)。在这种情形下,树仍是有效的。
        性质5也未受到威胁,尽管新节点N有两个黑色叶子子节点;
        但由于新节点N是红色,通过它的每个子节点的路径就都有同
        通过它所取代的黑色的叶子的路径同样数目的黑色节点,
        所以依然满足这个性质。
        */
        if(p->parent->color == RED){
            // 情形3
            if(p->uncle()->color == RED) {
                p->parent->color = p->uncle()->color = BLACK;
                p->grandparent()->color = RED;
                // 相当于在grandparent插入了一个红色节点
                insert_case(p->grandparent());
            } else {
                if(p->parent->rightTree == p && p->grandparent()->leftTree == p->parent) {
                    rotate_left(p);
                    p = p->leftTree;
                    goto left_left;
                    // 2020年6月3日17:43:52 感觉下面两句都有问题,改改
                    // p->color = BLACK;

                    // // 下面这一句有问题吧,万一其中一个是NIL...呢?? 怎么能标红?
                    // p->leftTree->color = p->rightTree->color = RED;
                } else if(p->parent->leftTree == p && p->grandparent()->rightTree == p->parent) {
                    rotate_right(p);
                    p = p->rightTree;
                    goto right_right;
                    // p->color = BLACK;
                    // p->leftTree->color = p->rightTree->color = RED;
                } else if(p->parent->leftTree == p && p->grandparent()->leftTree == p->parent) {
                    left_left:
                    p->parent->color = BLACK;
                    p->grandparent()->color = RED;
                    rotate_right(p->parent);
                } else if(p->parent->rightTree == p && p->grandparent()->rightTree == p->parent) {
                    right_right:
                    p->parent->color = BLACK;
                    p->grandparent()->color = RED;
                    rotate_left(p->parent);
                }
            }
        }
    }

    void DeleteTree(Node *p){
        if(!p || p == NIL){
            return;
        }
        DeleteTree(p->leftTree);
        DeleteTree(p->rightTree);
        delete p;
    }
public:

    bst() {
        NIL = new Node();
        NIL->color = BLACK;
        root = NULL;
    }

    ~bst() {
        if (root)
            DeleteTree (root);
        delete NIL;
    }

    void inorder() {
        if(root == NULL)
            return;
        inorder (root);
        cout << endl;
    }

    void insert (int x) {
        if(root == NULL){
            root = new Node();
            root->color = BLACK;
            root->leftTree = root->rightTree = NIL;
            root->value = x;
        } else {
            insert(root, x);
        }
    }

    bool delete_value (int data) {
        return delete_child(root, data);
    }
private:
    Node *root, *NIL;
};

int main(int argc, char const *argv[])
{
    bst rb_tree;

    return 0;
}

自己的学习图

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值