DAY09

红黑树

两个数据结构,一个是结点

class Node{
public:
    int _data;
    Color color;
    Node * left, * right, * parent;
    Node(int data)
        :_data(data){
        parent = left = right = nullptr;
        color = RED; // 新结点默认为红色
    }
};

一个是掌管整个红黑树的rbtree

// 红黑树
class Rbtree
{
private:
    Node * root = nullptr;// 根节点
    // 以当前结点为基准进行左旋操作
    void rotateLeft(Node * y);
    // 右旋操作
    void rotateRight(Node * y);
    // 从根节点开始插入,先区分左右子树
    // 然后将其左右子树的首结点作为根节点进行递归
    Node * BSTInsert(Node * root, Node * ptr);
    // 调整
    void fixViolation(Node * ptr);
public:
    // 插入,先插入再调整
    void insert(const int data);
    // 前序遍历
    // 使用双端队列
    void frontTraverse();
    // 层序遍历
    void seqTraverse();
};

红黑树最重要的思想就是,用二分查找的方式插入一个结点,这里有一个前提就是在插入结点之前,这个rbtree一定是一个红黑树。此外插入结点的颜色也是红色,因为红色结点不会影响层高,只需要做对应的调整即可。

// 从根节点开始插入,先区分左右子树
// 然后将其左右子树的首结点作为根节点进行递归
Node * BSTInsert(Node * root, Node * ptr)
{
    // 如果根节点为空,递归出口
    if(root == nullptr)
    {
        root = ptr;
    }
    // 左子树
    if(ptr->_data < root->_data)
    {
        // 递归
        root->left = BSTInsert(root->left, ptr);
        // 如果返回的是新节点,需要初始化它的父节点
        root->left->parent = root;
    }
    // 右子树
    else if(ptr->_data > root->_data)
    {
        // 递归
        root->right = BSTInsert(root->right,ptr);
        // 如果返回的是新节点,需要初始化它的父节点
        root->right->parent = root;
    }
    return root;
}

调整的思想:

红黑树有一条性质是不能有两个连续的红色结点,因为插入的新结点是红色,所以其父亲结点不能是红色结点,如果是红色结点就需要做调整,而黑色结点不需要。

while(ptr != root && ptr->color != BLACK && ptr->parent->color == RED)

如上循环条件为,ptr不等于根节点,如果是根节点,直接插入即可,不需要做调整。

其次保证当前指向结点的指针不能是黑色,因为插入的结点是红色的,如果当前的结点是黑色的,就意味着不会影响黑高也就没必要再网上递归了。

再然后就是当前结点的父节点是红色,如果是黑色就没有检查的必要了。

在循环判断以后就是对当前结点的父节点进行判断

// 如果父节点是祖父结点的左子节点
if(parent == grandParent->left)

如果是左子节点,就需要根据叔叔结点来判断类型,

如果叔叔结点是红色

此时将叔叔结点和父亲节点都设置为黑色,将祖父节点设置为红色,将祖父节点设置为当前结点,一直向上递归,直到祖父节点是红色(当前节点)。退出循环,判断如果是根节点就将根节点染成黑色。

if(uncle != nullptr && uncle->color == RED)
{
    grandParent->color = RED;// 将祖父结点染成红色
                             // 叔叔和父亲染成黑色
    uncle->color = BLACK;
    parent->color = BLACK;
    ptr = grandParent;// 将当前结点设置为祖父节点,下面的两层已经检查完毕,需要递归的向上检查
}

如果叔叔结点是黑色

旋转的主要目的:将一个结点的右子树或者左子树提升为该结点的父节点。

就需要根据是LR

先对ptr进行左旋,在进行右旋,将ptr旋转到根节点。

还是LL

只进行右旋。

进行调整

else
{
    // 祖父节点一定是黑色的
    // 如果结点是父节点的右孩子
    // 就形成了LR,即左右结构,需要先左旋再右旋
    if(ptr == parent->right)
    {
        // 左旋当前结点,将当前结点提升为根节点
        rotateLeft(ptr);
        parent = ptr->parent; // parent递归到上一级
    }
    // ptr是父节点的左孩子,LL结构,只需要右旋即可
    rotateRight(ptr); // 右旋当前结点
                      // 交换当前结点和祖父结点之间的颜色
    std::swap(ptr->color, grandParent->color);
    // 循环退出
    break;
}

如果当前结点的parent是grandParent的右孩子

逻辑翻转

如果叔叔结点是红色,操作一致

否则,根据RL和RR进行旋转,先右旋再左旋

最后再调用插入函数

void insert(const int data)
{
    Node * newNode = new Node(data);
    root = BSTInsert(root, newNode);
    // 调整
    fixViolation(newNode);
}

和层序遍历进行打印

// 层序遍历
void seqTraverse()
{
    deque<Node*> deq;
    // 将根节点入队列
    deq.push_back(root);
    int len = deq.size();
    while(deq.size() != 0)
    {
        for(int i = 0; i < len; i++)
        {
            // 出队
            Node* curr = deq.front();
            // 入队
            if(curr->left != nullptr)
                deq.push_back(curr->left);
            if(curr->right != nullptr)
                deq.push_back(curr->right);
            deq.pop_front();
            cout << curr->_data << "(" << curr->color << ")" << "\t";
        }
        len = deq.size();
    }
    cout << "\n";
}

由于国庆回家做了一天的火车,所以有两天没有更新,明天继续坚持,加油,今天手撕了一下红黑树,然后又get到了lambda的参数列表的正确用法。加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值