对红黑树的一些理解及实现

本文介绍了红黑树的基本概念,包括其特性、主要操作(变颜色、左右旋、插入和删除修正)以及查询功能。文章还探讨了插入和删除过程中如何维护红黑树的平衡,并提供了代码实现链接。此外,作者分享了测试红黑树正确性的方法,并预告了实现set和map的内容。
摘要由CSDN通过智能技术生成

对红黑树的一些理解及实现

红黑树(英语:Red–black tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。

以下是我对红黑树的一些理解,可供参考,不保证准确性。
参考资料:
https://www.cnblogs.com/alantu2018/p/8462017.html
https://zhuanlan.zhihu.com/p/22800206

最后实现的代码放在Github上:
https://github.com/Radium1209/Red-Black-Tree

特性

  1. 每个节点是黑色或红色(所以叫红黑树)
  2. 根节点是黑色的
  3. 叶节点是黑色的(空的叶节点)
  4. 如果一个节点为红,子节点必须为黑
  5. 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点
  • 为什么根节点是黑色的?
    我的理解是使前两层一定满足红黑树条件,不然在前两层就需要进行旋转操作(见后文),这样会使旋转操作更加复杂(因为要考虑祖父节点不存在),且旋转后的根节点也会变黑,因此不如一开始就令根节点为黑。
  • 为什么叶子节点是黑色的?
    这个和后面旋转时操作的叔叔节点有关,因为叔叔节点为空的话也为黑色,这样满足其中一个旋转条件。
  • 特性4和特性5
    这两个特性是用来保持平衡的,红黑树是一种近似平衡的树,特性5保证了没有一条路径会比其他路径长出2倍,所以可以保持红黑树的近似平衡。红黑树的近似平衡是因为它的旋转操作会比较少(与AVL树相比),所以红黑树在插入删除的性能要好于AVL树,在查询上可能会略逊于AVL树。
    红黑树示意图

主要操作

  1. 变颜色
  2. 左旋
  3. 右旋

变颜色

就是改变一下颜色,总共就两种。

左旋

关于旋转的部分网上的资料很多,可以自行搜索,这边主要讲一下关于代码的部分。

 左旋示意图(对节点x进行左旋):
      px                              px
     /                               /
    x                               y                
   /  \      --(左旋)-->           / \                
  lx   y                          x  ry     
     /   \                       /  \
    ly   ry                     lx  ly  

以上图为例,左旋所改变的部分是:

 px->Child = y	// 可能是左孩子也有可能是右孩子
 x->father = y	// x的父亲和右儿子都变了
 x->rightChild = ly
 y->father = px	// y的父亲和左儿子都变了
 y->leftChild = x
 ly->father = x	// ly的父亲变了

所以我们的代码就直接改变这些变化的值就可以了,不过需要注意一些值可能为空:

template<class T>
void RBTree<T>::leftRotate( RBTNode<T> *node )
{
   
    RBTNode<T> *x = node;
    RBTNode<T> *y = x->rightChild;

    // 修改x->rightChild = ly
    x->rightChild = y->leftChild;
    
    // 修改ly->father = x, 注意ly可能为空
    if ( y->leftChild != NULL)
    {
   
        y->leftChild->father = x;
    }
    
    // 修改y->father = px
    y->father = x->father;

    // 修改px-Child = y,此时需要考虑px为NULL以及左右孩子的区别
    if ( x->father == NULL )
    {
   
        root = y;   // 此时要更新root
    }
    else 
    {
   
        if ( x->father->leftChild == x )
        {
   
            x->father->leftChild = y;
        }
        else
        {
   
            x->father->rightChild = y;
        }
    }

    // 修改y->leftChild = x
    y->leftChild = x;
    
    // 修改x->father = y
    x->father = y;
}

右旋

和左旋基本上一样:

template<class T>
void RBTree<T>::rightRotate( RBTNode<T>* node )
{
   
    RBTNode<T> *y = node;
    RBTNode<T> *x = y->leftChild;

    y->leftChild = x->rightChild;
    
    if ( x->rightChild != NULL )
    {
   
        x->rightChild->father = y;
    }

    x->father = y->father;

    if ( y->father == NULL )
    {
   
        root = x;
    }
    else
    {
   
        if ( y->father->leftChild == y )
        {
   
            y->father->leftChild = x;
        }
        else
        {
   
            y->father->rightChild = x;
        }
    }

    x->rightChild = y;

    y->father = x;
}

插入

正常插入

以上操作都是为了插入和删除做准备的,因为在插入和删除的时候会使红黑树不满足红黑树的特性,所以需要用以上操作对红黑树进行修正。下面讲一下在插入时如何对红黑树进行修正。
首先按照正常的二叉搜索树进行插入,且插入的节点为红色。

  • 插入的节点为什么是红色?
    如果插入的是黑色永远不会破坏特性4,而且所有节点都变成黑色了,根本没有意义。
/* 插入 */
template<class T>
void RBTree<T>::insert( RBTNode<T>* node )
{
   
    RBTNode<T> *y = NULL;
    RBTNode<T> *x = root;

    // 当做二叉排序树正常插入
    while ( x != NULL )
    {
   
        y = x;
        if ( node->value < x->value )
        {
   
            x = x->leftChild;
        }
        else
        {
   
            x = x->rightChild;
        }
    }

    node->father = y;
    if ( y != NULL )
    {
   
        if ( node->value < y->value)
        {
   
            y->leftChild = node;
        }
        else
        {
   
            y->rightChild = node;
        }
    }
    else
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值