红黑树的C++实现

在实现之前网上浏览了一下,看到很多人都有自己的实现.主要的实现方式都是受了Introduction to Algorithm的影响.代码的结构和书中给出的伪码如出一辙,但是问题大都是1,没有深刻的理解FixUp和到底哪个节点需要. 2,忽略了Sentinel也就是哨兵节点的存在的意义.C/C++实现中的NULL在指代上还是出现了一点偏差.

这里我们比较关心插入和删除操作(查询和普通的BST没有差别,不赘述了)
现在我们来具体分析一下插入操作
1. 首先父亲节点如果是黑色节点的话,不需要处理了,多一个红色节点不会有任何影响.
2. 如果父亲节点是红色的,而叔父节点也是红色,那就是把父亲和叔父节点同时改成黑色,然后递归的向上传递.
3. 如果父亲节点是红色的,而叔父节点是黑色的. 后面的话比较拗口.父亲是祖父的左孩子.
   (1)如果自己是父亲的左孩子,那么修改父亲和祖父的颜色然后右转祖父
   (2)如果自己是父亲的有孩子,那么先做左转父亲,在把自己指向父亲,执行(1).
类似的描述很多地方都有,这个过程也是比较清晰,所以不存在什么错误.而且比较清楚的问题就是需要FixUp的就是当前新加入的节点.

问题呢集中体现在删除的地方.这个是网上看到的一个实现,这里一样,也遵循了算法导论中的命名方式:p,x,y分别有了自己的定义:
    RBTreeNode *p = NULL; //指向查找到的节点(实际上可能并不删除它)
    RBTreeNode *x = NULL; //指向欲删除的节点(可能和p一样)
    RBTreeNode *y = NULL; //指向欲删除节点的子节点(欲删除节点只有一个子节点)
但是在很多情况下y都是NULL,(当然实际意义上是哨兵节点啦). 所以看到很多代码选择了FixUp X节点的父亲节点.但实际上
这个意义其实是完全不一样的 我们来看一个我碰到的具体的例子就很明白了,一棵树在插入了1-20以后呈现出这样的形态

比如我现在要删除10这个节点,那么显然p就是10这个节点,而x是什么的,x就是max(p->left)或者min(p->right).现在这个例子中就是9或者11,我们假定选择9.而y是什么呢就是9的子节点(也即是我们说的哨兵节点).那这个时候需要FixUp的节点是什么呢??

由网上的一些实现看来是
            if (y != NULL)
            {
                DeleteFixup(y);
            }
            else
            {
                DeleteFixup(x->parent);
            }
X的父亲,也就是10这个节点,这样的话,兄弟节点的右子节点为红色,直接进入了Delete Case4做改色(12改成黑色,16红色18黑色),然后左旋转12.这个时候我们发现,转了以后并不平衡,显然违反了红黑树的定义,在12-9-11这个分支上有连续3个黑色节点,显然多余了其他的12-14-13和12-14-15. 而造成这一切的元凶就是FixUp的错误的开始.

   其实一样,我们还是应该按照定义来,还是从y开始更新,但是y表面上看起来是NULL,怎么FixUp呢?这个时候哨兵节点的作用得以体现,我们把每个叶子节点的left/right都指向一个哨兵节点,哨兵节点的颜色是黑色,而且left/right就是NULL,这样我们就可以用这个哨兵节点来做FixUp了.同时省去了在Fixup中冗余的NULL的判断,这里的指针就一直是有效的了.所以在这个case中我们实际上下一个w就是右边的黑色哨兵节点,所以x和w都是黑色,所以进入了deleteFixUp的case2,只有把11改成了红色之后才继续向上递归的FixUp.这个实现其实给我很多的感触.人的烦恼有很多,但无非是生活是复杂的,你把他想简单了,或者生活是简单的,你把他想复杂了:)

最后要说一点,在插入和删除操作有会有可能导致红黑树出现不平衡的情况,这个时候需要FixUp,但是最合理的实现是在操作结束后加入一段debug assert的代码用以检查平衡度的正确性,至少可以避免明显错误的尴尬:)
最后给出了所有的代码:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值