红黑树——【C++实现】

29 篇文章 1 订阅
14 篇文章 0 订阅

在这里插入图片描述

本章代码gitee仓库:红黑树

🎈1. 红黑树概念

红黑树(red-black-tree)也是一种二叉搜索树,每个节点上增添了一个存储位表示节点颜色(Red or Black)。通过每条路径上根到叶子的各节点颜色限制,确保最长路径不超过最短路径二倍,达到近似平衡(平衡的限制规则没有AVL树那么严格)。

image-20230911154830986

红黑树性质:

  1. 根结点和所有外部结点(空结点)的颜色的都是黑色
  2. 如果一个节点是红色的,那么它的孩子节点肯定是黑色的(任何路径没有连续的红色节点)
  3. 从根结点到外部结点的路径上都有相同数目的黑色节点(每条路径的黑色节点数量相同)

🏞2.红黑树的实现

😎2.1 红黑树结构

enum Colour
{
	RED,
	BLACK
};

template<class K,class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	pair<K, V> _kv;
	Colour _col;

	RBTreeNode(const K& key)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(key)
		, _col(RED)
	{}
};

template<class K,class V>
struct RBTree
{
	typedef RBTreeNode Node;

public:
	//接口

private:
	Node* _root;
};

😎2.2 插入

红黑树的插入节点,默认是红色

Tips:

如果插入节点默认为黑色,那么这颗树整体就被影响了,该路径上的黑色节点与其他路径黑色节点数量不同

如果插入节点默认为红色,那么就算要影响,也是只影响该路径,其他路径不受影响

我们这里设当前节点为cur,父亲节点为parent,父亲的兄弟节点为uncle

如果cur为黑,就不用管了,所有只当cur为红的时候,才考虑调整红黑树

情况1:cur为红,parent为红,grandfather为黑,uncle存在且为红

image-20230911175852058

解决方式:

pu改为黑,g改为红,然后把g当为cur,继续向上调整

情况2:cur为红,parent为红,grandfather为黑,uncle不存在/存在且为黑

uncle情况分为2种:

  1. uncle不存在

    如果uncle节点不存在,那么cur必定是插入节点,如果cur不是插入节点,那么curparent一定有一个是黑色节点,这样就不符合每条路径黑色节点个数相同这个规则

  2. uncle存在且为黑

    如果uncle为黑,那么cur原先也一定为黑,下图cur为红色是cur子树调整过程中将cur颜色修改为红色

image-20230912102819191

解决方式:

parentgrandfather的左孩子,右旋;

parentgrandfather的右孩子,左旋

parent变黑,grandfather变红

如果是双旋,cur变黑,grandfather这次情况下,都是要变红

代码实现:

bool Insert(const pair<K, V>& kv)
{
    if (_root == nullptr)
    {
        _root = new Node(kv);
        _root->_col = BLACK;
        return true;
    }

    Node* parent = nullptr;
    Node* cur = _root;
    while (cur)
    {
        if (cur->_kv.first > kv.first)
        {
            parent = cur;
            cur = cur->_left;
        }
        else if (cur->_kv.first < kv.first)
        {
            parent = cur;
            cur = cur->_right;
        }
        else
        {
            return false;
        }
    }

    cur = new Node(kv);
    cur->_col = RED;

    //链接
    if (parent->_kv.first > cur->_kv.first)
    {
        parent->_left = cur;
    }
    else
    {
        parent->_right = cur;
    }
    cur->_parent = parent;

    //变色
    while (parent && parent->_col == RED)	//父亲存在且为红就要变色
    {
        Node* grandfather = parent->_parent;	//祖父节点
        if (parent == grandfather->_left)
        {
            Node* uncle = grandfather->_right;
            //情况1:uncle存在且为红
            if (uncle && uncle->_col == RED)
            {
                //变色
                //父亲、叔叔变黑
                parent->_col = uncle->_col = BLACK;
                //祖父变红
                grandfather->_col = RED;

                //继续向上调整
                cur = grandfather;
                parent = cur->_parent;

            }
            else	//叔叔为黑 or 叔叔不存在
            {
                if (cur == parent->_left)
                {
                    //		g             p
                    //	   /             / \
                    //    p      ==》   c   g
                    //   /
                    //  c
                    //右旋
                    RotateR(grandfather);
                    parent->_col = BLACK;
                    grandfather->_col = RED;
                }
                else
                {
                    //		g           g          c
                    //	   /           /          / \
                    //    p    ==》   c   ==》   p    g
                    //     \         /
                    //      c       p
                    //左右双旋
                    RotateL(parent);
                    RotateR(grandfather);
                    cur->_col = BLACK;
                    grandfather->_col = RED;

                }
                break;
            }
        }
        else	//parent == grandfather->_right
        {
            Node* uncle = grandfather->_left;
            //情况1:同上
            if (uncle && uncle->_col == RED)	//叔叔存在且为红
            {
                //叔叔父亲变黑
                parent->_col = uncle->_col = BLACK;
                //祖父变红
                grandfather->_col = RED;

                //继续向上调整
                cur = grandfather;
                parent = cur->_parent;
            }
            else	//叔叔不存在 or 存在且为黑
            {
                if (cur == parent->_right)
                {
                    //	   g                p
                    //	    \              / \
                    //       p      ==》  g   c
                    //        \
                    //         c
                    //左旋
                    RotateL(grandfather);
                    grandfather->_col = RED;
                    parent->_col = BLACK;
                }
                else
                {
                    //		g           g            c
                    //	     \           \          / \
                    //        p    ==》   c   ==》 g    p
                    //       /             \
                    //      c               p
                    //右左双旋
                    grandfather->_col = RED;
                    cur->_col = BLACK;
                }
                break;
            }
        }
    }
    _root->_col = BLACK;
    return true;
}

旋转操作

//左旋
void RotateL(Node* parent)
{
    Node* cur = parent->_right;
    Node* curLeft = cur->_left;

    parent->_right = curLeft;
    if (curLeft)
    {
        curLeft->_parent = parent;
    }
    cur->_left = parent;
    Node* ppnode = parent->_parent;
    parent->_parent = cur;

    if (ppnode == nullptr)
    {
        _root = cur;
        cur->_parent = nullptr;
    }
    else
    {
        if (ppnode->_left == parent)
        {
            ppnode->_left = cur;
        }
        else
        {
            ppnode->_right = cur;
        }
        cur->_parent = ppnode;
    }
}

//右单旋
void RotateR(Node* parent)
{
    Node* cur = parent->_left;
    Node* curRight = cur->_right;

    parent->_left = cur->_right;
    //防止curRight为空
    if (curRight)
    {
        curRight->_parent = parent;
    }

    cur->_right = parent;
    //保存父亲的父亲节点
    Node* ppnode = parent->_parent;
    parent->_parent = cur;

    if (ppnode == nullptr)	//父亲的父亲为空,则为根节点
    {
        _root = cur;
        cur->_parent = nullptr;
    }
    else
    {
        if (ppnode->_left == parent)
        {
            ppnode->_left = cur;
        }
        else
        {
            ppnode->_right = cur;
        }
        cur->_parent = ppnode;
    }
}

😭2.3 删除

参考《数据结构——用面向对象方法与C++语言描述》

image-20230913105527580

代码实现:

这个是gpt写的,还是有bug

GitHub上面平衡树类的实现:BalanceTree

现在不啃了,沉淀沉淀再来看看,打个标记先2023/9/13

bool _Erase(const pair<K, V>& kv)
{
    Node* parent = nullptr;           // 父节点指针
    Node* cur = _root;                // 当前节点指针
    Node* double_black_node = nullptr; // 用于处理双黑节点的指针

    // 先查找要删除的节点
    while (cur)
    {
        if (cur->_kv.first > kv.first)
        {
            parent = cur;
            cur = cur->_left; // 向左子树移动
        }
        else if (cur->_kv.first < kv.first)
        {
            parent = cur;
            cur = cur->_right; // 向右子树移动
        }
        else
            break; // 找到要删除的节点
    }

    // 元素不存在
    if (cur == nullptr)
        return false;

    // 删除节点为叶子或者只有一个子节点
    Node* u = nullptr;
    if (cur->_left)
    {
        u = cur->_left;
    }
    else if (cur->_right)
    {
        u = cur->_right;
    }

    // 删除节点为叶子或者只有一个子节点
    if (cur->_col == RED) // 删除的节点为红色,直接删除
    {
        if (parent)
        {
            if (parent->_left == cur)
            {
                parent->_left = u;
            }
            else
            {
                parent->_right = u;
            }
        }
        else
        {
            // 如果删除的是根节点
            _root = u;
            if (_root)
                _root->_col = BLACK;
        }
        // 删除
        delete cur;
        cur = nullptr;
        return true;
    }

    // 删除节点为黑,进行调整
    if (parent)
    {
        if (parent->_left == cur)
        {
            parent->_left = u;
        }
        else
        {
            parent->_right = u;
        }
    }
    else
    {
        // 如果删除的是根节点
        _root = u;
        if (_root)
            _root->_col = BLACK;
    }

    // 删除
    delete cur;
    cur = nullptr;

    // 处理双黑节点
    Node* sibling;
    while (u != _root && (!u || u->_col == BLACK))
    {
        if (parent->_left == u)
        {
            sibling = parent->_right;
            if (sibling->_col == RED)
            {
                // 情况1:兄弟节点为红色
                sibling->_col = BLACK;
                parent->_col = RED;
                RotateL(parent);
                sibling = parent->_right;
            }
            if ((!sibling->_left || sibling->_left->_col == BLACK) && (!sibling->_right || sibling->_right->_col == BLACK))
            {
                // 情况2:兄弟节点及其子节点都为黑色
                sibling->_col = RED;
                u = parent;
                parent = u->_parent;
            }
            else
            {
                if (!sibling->_right || sibling->_right->_col == BLACK)
                {
                    // 情况3:兄弟节点的右子节点为黑色
                    sibling->_left->_col = BLACK;
                    sibling->_col = RED;
                    RotateR(sibling);
                    sibling = parent->_right;
                }
                // 情况4:兄弟节点的右子节点为红色
                sibling->_col = parent->_col;
                parent->_col = BLACK;
                sibling->_right->_col = BLACK;
                RotateL(parent);
                u = _root;
            }
        }
        else
        {
            sibling = parent->_left;
            if (sibling->_col == RED)
            {
                // 情况1:兄弟节点为红色
                sibling->_col = BLACK;
                parent->_col = RED;
                RotateR(parent);
                sibling = parent->_left;
            }
            if ((!sibling->_left || sibling->_left->_col == BLACK) && (!sibling->_right || sibling->_right->_col == BLACK))
            {
                // 情况2:兄弟节点及其子节点都为黑色
                sibling->_col = RED;
                u = parent;
                parent = u->_parent;
            }
            else
            {
                if (!sibling->_left || sibling->_left->_col == BLACK)
                {
                    // 情况3:兄弟节点的左子节点为黑色
                    sibling->_right->_col = BLACK;
                    sibling->_col = RED;
                    RotateL(sibling);
                    sibling = parent->_left;
                }
                // 情况4:兄弟节点的左子节点为红色
                sibling->_col = parent->_col;
                parent->_col = BLACK;
                sibling->_left->_col = BLACK;
                RotateR(parent);
                u = _root;
            }
        }
    }

    if (u)
        u->_col = BLACK;

    return true;
}

😎2.4 树的高度

int _Height(Node* root)
{
    if (root == nullptr)
        return 0;
    int leftH = _Height(root->_left);
    int rightH = _Height(root->_right);

    return max(leftH, rightH) + 1;
}

😎2.5 是否为红黑树

这里采用顺便一条路径的黑色节点作为基准值,判断每条路径的黑色节点是否相同

bool CheckColour(Node* root, int blackNum, int benchmark)
{
    if (root == nullptr)
    {
        if (blackNum != benchmark)
            return false;

        return true;
    }

    if (root->_col == BLACK)
        blackNum++;

    if (root->_col == RED && root->_parent && root->_parent->_col == RED)
    {
        cout << root->_kv.first << "连续红色节点" << endl;
        return false;
    }

    return CheckColour(root->_left, blackNum, benchmark) && CheckColour(root->_right, blackNum, benchmark);
}

//是否为红黑树
bool _IsBanlance(Node* root)
{
    if (root == nullptr)
        return true;

    if (root->_col != BLACK)
        return false;

    //找容易一条路径的黑色节点个数为基准值 最左为例
    int benchmark = 0;
    Node* cur = _root;
    while (cur)
    {
        if (cur->_col == BLACK)
            benchmark++;
        cur = cur->_left;
    }

    return CheckColour(root, 0, benchmark);
}

😎2.6 红黑树遍历

void _InOrder(Node* root)
{
    if (root == nullptr)
        return;

    _InOrder(root->_left);
    cout << root->_kv.first << " ";
    _InOrder(root->_right);
}

C++STL库里面的mapset的底层就采用红黑树,大佬写的很棒,我们了解底层之后,直接拿来用即可,学有余力可以啃一啃源码

那么本次分享就到这里,我们下期再见,如果还有下期的话

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加法器+

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值