C++手撕红黑树

红黑树的性质

/*
    1、每个节点是红色的或者黑色的
    2、根节点是黑的
    3、每个叶子节点是黑的
    4、如果一个节点是红的,则它的两个儿子是黑的
    5、对每个节点,从该节点到其子孙节点所有路径上的包含相同数目的黑节点
*/

//红黑树与平衡二叉树的区别与优点

// 红黑树黑高保证了平衡  而平衡二叉树在实现上每个节点需要记录当前节点的高度 每次插入都需要判断高度差 比较麻烦

#include<iostream>

#define RED 0
#define BLACK 1


//红黑树与平衡二叉树的区别与优点

// 红黑树黑高保证了平衡  而平衡二叉树在实现上每个节点需要记录当前节点的高度 每次插入都需要判断高度差 比较麻烦

template<typename K, typename V>
class RBtree;

template<typename K, typename V>
class RBtreeNode{
public:
    RBtreeNode() = default;

    RBtreeNode(K key, V value):lch(nullptr), rch(nullptr), parent(nullptr), color(RED){
        kv.first = key;
        kv.second = value;
    }

    RBtreeNode<K, V>* lch;
    RBtreeNode<K, V>* rch;
    RBtreeNode<K, V>* parent;
    int color;
    std::pair<K, V> kv;
}; 

template<typename K, typename V>
class RBtree{
public:
    RBtree() {
        leaf = new RBtreeNode<K, V>;
        leaf->color = BLACK;
        root = this->leaf;
    }

    //中序遍历输出该树内容
    void Print(RBtreeNode<K,V>* node){
        if(node != leaf){
            Print(node->lch);
            std::cout<< "key = " << node->kv.first << "\tvalue = " << node->kv.second << std::endl;
            Print(node->rch);
        }
    }
private:
    //左旋
    void left_rotate(RBtreeNode<K,V>* x){
        //对x进行左旋 
        //如果y不是叶子节点
        RBtreeNode<K, V>* y = x->rch;
        //第一次  
        //1.1
        x->rch = y->lch;
        //1.2
        if(y->lch != this->leaf){
            y->lch->parent = x;
        }
        //第二次
        y->parent = x->parent;
        //x本身没有父节点 那么y成为新的根节点
        if(x->parent==this->leaf){
            this->root = y;
        }
        //x有父节点
        //并且x是父亲的左孩子
        else if(x == x->parent->lch){
            x->parent->lch = y;
        }//x是父亲的右孩子
        else if(x == x->parent->rch){
            x->parent->rch = y;
        }
        //第三次
        y->lch = x;  //3.1
        x->parent = y; //3.2
    }
    //右旋
    void right_rotate(RBtreeNode<K,V>* y){
        RBtreeNode<K,V>* x = y->lch;
        //1
        y->lch = x->rch;
        if(x->rch != this->leaf){
            x->rch->parent = y;
        }
        //2
        x->parent = y->parent;
        if(y->parent==this->leaf){
            this->root  = x;
        }
        else if(y == y->parent->lch){
            y->parent->lch = x;
        }
        else if(y == y->parent->rch){
            y->parent->rch = x;
        }
        //3 
        x->rch = y;
        y->parent = x;     
    }

    //记住一下几点:
    //1、插入调整之前,该树是一颗红黑树 
    //2、插入节点是红色 这样不会影响黑高的属性
    //调整操作
    void adjust(RBtreeNode<K, V>* node){
        //如果插入节点的父节点是红色 递归调整
        //如果父亲节点是红色 那么祖父节点肯定是黑色 
        while(node->parent->color == RED){
            //如果父节点是祖父节点的左子树
            if(node->parent == node->parent->parent->lch){
                RBtreeNode<K,V>* uncle = node->parent->parent->rch;
                //如果叔叔节点是红色 说明叔叔节点存在 不用旋转 只需要变色
                if(uncle->color == RED){
                    node->parent->color = BLACK;
                    uncle->color = BLACK;
                    node->parent->parent->color = RED;
                    //递归
                    node = node->parent->parent;
                }
                //叔叔节点是黑色 说明叔叔节点根本就不存在 是叶子节点 
                //不然没法保持红黑树的平衡 这时候需要旋转加变色
                else{
                    //如果该节点是父节点的右节点 需要先左旋
                    if(node == node->parent->rch){
                        node = node->parent;
                        this->left_rotate(node);
                    }
                    node->parent->color = BLACK;
                    node->parent->parent->color = RED;
                    this->right_rotate(node->parent);
                }
            }
            //第二种情况
            else if(node->parent == node->parent->parent->rch){
                RBtreeNode<K, V>* uncle = node->parent->parent->lch;
                //只需要变色
                if(uncle->color == RED){
                    node->parent->color = BLACK;
                    uncle->color = BLACK;
                    node->parent->parent->color = RED;
                    node = node->parent->parent;
                }
                //需要变色+旋转
                else{
                    //这种情况需要先将node的父节点右旋一次
                    //如果进入了if 经过右旋后 node也是最底下的节点
                    if(node==node->parent->lch){
                        node = node->parent;
                        this->right_rotate(node);
                    }
                    //不管进不进if node始终都表示最下面的一个节点
                    node->parent->color = BLACK;
                    node->parent->parent->color = RED;
                    this->left_rotate(node->parent->parent);
                }
            }
        }
        //每次将根节点变为黑色
        this->root->color = BLACK;
    }
    
public:
    //插入操作
    void insert(RBtreeNode<K, V>* node){
        //利用z节点遍历
        RBtreeNode<K, V>* z = this->root;
        //利用一个节点始终保存z节点的父节点
        RBtreeNode<K, V>* pre = this->leaf;
        while(z != this->leaf){
            pre = z;
            if(node->kv.first < z->kv.first){
                z = z->lch;
            }
            else if(node->kv.first > z->kv.first){
                z = z->rch;
            }
            else if(node->kv.first == z->kv.first){
                return;
            }
        }
        node->parent = pre;
        //如果这棵数是空的 root一开始就等于叶子节点 说明pre根本没变化
        if(pre == this->leaf){
            this->root = node;
        }
        else if(node->kv.first < pre->kv.first){
            pre->lch = node;
        }
        else if(node->kv.first > pre->kv.first){
            pre->rch = node;
        }
        node->lch = this->leaf;
        node->rch = this->leaf;
        //默认添加红色 不影响黑高的属性
        node->color = RED;

        adjust(node);
    }
    

public:
    RBtreeNode<K,V>* root;
    //如果一个节点左右节点都是这片区域 表明这是叶子节点
    RBtreeNode<K,V>* leaf;
};


int main(){
    RBtree<int, int> t;
    RBtreeNode<int, int>* n1 = new RBtreeNode<int, int>(60, 10);
    RBtreeNode<int, int>* n2 = new RBtreeNode<int, int>(50, 20);
    RBtreeNode<int, int>* n3 = new RBtreeNode<int, int>(70, 52);
    RBtreeNode<int, int>* n4 = new RBtreeNode<int, int>(90, 224);
    RBtreeNode<int, int>* n5 = new RBtreeNode<int, int>(20, 2320);
    RBtreeNode<int, int>* n6 = new RBtreeNode<int, int>(130, 2320);
    RBtreeNode<int, int>* n7 = new RBtreeNode<int, int>(180, 13124);

    t.insert(n1);
    t.insert(n2);
    t.insert(n3);
    t.insert(n4);
    t.insert(n5);
    t.insert(n6);
    t.insert(n7);

    t.Print(t.root);
    return 0;
}

总结一下:

这里目前只实现了插入功能,删除功能还没实现

打印是按中序遍历输出 由于红黑树也有平衡二叉树的性质 所以中序遍历是递增序列

红黑树的插入注意的几个地方:

1、每次插入的节点 默认是红色 因为红黑树黑高的特性 插入节点为红色 不会改变这一特性

2、当插入的节点父节点为红色时 这时候需要循环的调整

调整需要根据情况判断

首先是判断当前插入节点的父节点是祖父节点的左节点还是右节点

其次需要判断叔父节点是黑色还是红色

如果叔父节点是红色 说明叔父节点是存在的 这样插入节点过后并不会影响平衡的特性, 只需要进行变色操作就行, 变色操作就是将父节点与叔父节点变为黑色 然后将祖父节点变为红色 之后递归的检查

如果叔父节点是黑色,(注意此时 父节点是红色,祖父节点是黑色 这个是已知的) 由于在插入节点前,同样是一颗红黑树, 所以叔父节点这时候只可能是叶子节点, 不然不会满足红黑树的特性(黑高), 所以这时候需要进行旋转操作与变色操作

旋转操作同样需要根据情况判断,有旋转一次与两次的区别。 最后是变色。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值