算法导论的学习--第十二章、十三章 二叉搜索树与红黑树

算法导论(第三版) 第十二章、十三章 二叉搜索树与红黑树

前言

本人是计算机专业学渣一枚,因前面算法课上课神游太虚,因此现在亡羊补牢,本文主要是本人对于学习过程的记录,说的不对的地方欢迎指正。

一、二叉搜索树

二叉搜索树的定义比较简单,本质上就是一颗二叉树。不过不同于一般的二叉树,二叉搜索树有它自己的性质:对每个节点来说,它的左子树的所有节点的关键字都不大于它的关键字,它的右子树的所有节点的关键字都不小于它的关键字,注意这里不是仅仅要求左右孩子,而是要求左右子树,这是比较容易记混的,也是比较容易对二叉搜索树的定义产生误解的地方。(前面第六章当中介绍到的堆是只对自己的孩子有大小要求)

二叉搜索树有三种遍历的方式,前序遍历,中序遍历,后序遍历,比较容易记忆的方法是:当前节点遍历的位置分别位于前面、中间和最后。
前序遍历:本节点–左子节点–右子节点
中序遍历:左子节点–本节点–右子节点
后序遍历:左子节点–右子节点–本节点

这三种遍历方式有什么具体的应用我目前还不是很清楚。

接下来是二叉搜索树的一些具体操作:
1、搜索:搜索的流程比较简单,将被搜索的关键字作为输入,从根节点进行递归查询,如果关键字比它本身的关键字小,就进入左子树进行查询;如果关键字相等查询结束;反之,进入右子树进行查询

2、找寻关键字最大(小)的节点:一直沿着右(左)孩子进行查询,直到找到一个叶节点,返回

3、找寻当前节点的前驱(后继):找寻当前以节点的左(右)孩子为根节点的子树的关键字最大(最小)的节点。前驱是关键字小于当前节点的所有节点当中关键字最大的节点;后继是关键字大于当前节点的所有节点当中关键字最小的节点。主要在后面的删除当中有所应用

4、替换:在删除当中需要用到的操作,这里的替换操作是用一棵子树来替换另一棵子树。代码也非常简单,如果当前子树是它的父亲的左子树,那么就把新的子树改成它父亲的左子树,反之改成右子树

5、插入:二叉搜索树的插入的关键在于找到一个合适的空位置供新的节点进行插入,这个查找的过程就类似于上面的搜索过程,关键字小就走左子树,反之走右子树,找到一个合适的空位之后就可以进行插入操作了。

6、删除:删除操作是所有操作当中最为复杂的,因为删除需要考虑多种情况,细分可以分为三种case:
case1:被删除的节点没有子节点
case2:被删除的节点只有一个子节点
case3:被删除的节点有两个子节点

在代码当中,case1是可以被省略的,可以通过case2的代码进行覆盖。因此在代码当中,其实只需要考虑两种情况:case2和case3。

case2的处理相对比较简单,因为被删除的节点只有一个子节点,无论它是左孩子还是右孩子,只需要进行一次替换就可以了(不会违背二叉搜索树的任何性质);

case3的处理相对就会复杂一些,因为被删除的节点有两个子节点,为了不破坏二叉搜索树既有的性质(对每个节点来说,它的左子树的所有节点的关键字都不大于它的关键字,它的右子树的所有节点的关键字都不小于它的关键字),我们就必须找一个合适的子节点来替代这个被删除节点位置,可以思考一下这个子节点需要在哪进行查找呢?

其实既可以在左子树上找也可以在右子树上找,因为无论是被删除节点的前驱还是后继,都可以替换该被删除节点,并且不会破坏二叉搜索树的既有性质。这里可以思考一下为什么,还是比较好理解的。

具体到代码当中的处理:找到前驱(后继)后,如果它不是被删除节点的孩子节点,将它的左子树(右子树)替换掉它本身,否则不进行这一步操作,然后用它来替换被删除的节点。

到这里二叉搜索树基本就介绍完了,二叉搜索树的操作都是和它的高度相关的(高度为h),例如插入和删除的时间复杂度都是O(h)

二、红黑树

红黑树其实可以看作是二叉搜索树的进化版,因为当大量的节点集中在一条斜线上时,二叉搜索树的操作会比较缓慢(因为树高比较高)。为了避免这种情况的发生,就对每个节点引入了颜色这个概念,红黑树就这么产生了。

红黑树是一种平衡树,它保证了在最坏情况下,树上的操作的时间复杂度也能控制在O(logn)上。因为对一棵平衡二叉树来说,它的树高是不会超过logn的。

红黑树具备几个比较特殊的性质:
1、每个节点的颜色是红色或者黑色
2、如果一个节点的颜色是红色,那么它的两个孩子的颜色一定是黑色
3、它满足二叉搜索树的性质
4、每个叶节点都是黑色的(红黑树上的叶节点一定是空节点,和二叉搜索树不同,红黑树用黑色的空节点替换了null)
5、对每个节点来说,它到它所有的后代叶节点的简单路径上有相同数量的黑色节点

红黑树的这些性质使得红黑树成为了二叉平衡树,但是也让插入、删除这些操作变得更加复杂。

在介绍红黑树的插入和删除操作之前,得先了解另外两个非常重要的操作:左旋和右旋。这两者对于红黑树的插入和删除非常重要。左旋和右旋本身并不复杂,如图所示:
红黑树的左旋和右旋

插入操作:
在介绍插入操作之前,得先弄明白一个简单但重要的问题,为什么新节点的颜色一定得是红色的,这个问题不难弄懂,考虑考虑性质5就可以了。

红黑树的插入操作本质上和二叉搜索树的插入操作是相同的,不过多了一个修正的过程

那么在插入了新节点之后,就得考虑一个问题了,新插入的红色节点在什么情况下会破坏红黑树的既有性质,从而需要进行修正呢?

答案是只有在新节点的父亲节点的颜色是红色时才会出现问题(违背了性质2)。那么在这种情况下该如何进行修正呢?

要知道在父亲节点的颜色为红色的情况下,祖父节点一定是黑色的。那么这里就分为几种情况了(这里假设父亲节点是祖父的左孩子):
1、父亲节点的兄弟节点是红色节点,而且自己是左孩子
2、父亲节点的兄弟节点是红色节点,而且自己是右孩子
3、父亲节点的兄弟节点是黑色节点,而且自己是左孩子
4、父亲节点的兄弟节点是黑色节点,而且自己是右孩子

如果是1、2两种情况,处理会比较简单,因为只需要同时变更父亲和父亲节点的兄弟的颜色为黑色,祖父节点为红色即可,然后再从祖父节点上接着进行检查。

如果为3、4两种情况,处理则会相对复杂一些,这里需要思考的地方其实很多,想要充分的理解为什么算法导论当中的伪代码是那样处理的,就必须自己拿纸好好演示下3、4两种情况。我先给出结论,算法导论当中的处理是通过一个旋转将情况4变成了情况3,然后按照情况3进行处理。

那么这里就衍生了三个问题:1、为什么1、2可以通过直接变更颜色,而3、4不行;2、情况3是怎么处理的;3、为什么情况4不能直接处理。

第一个问题是比较好理解的,在插入之前,这棵树是一个正常的红黑树,也就是说从祖父节点开始,到所有叶节点的黑色节点的数量是一样的。而且父亲和他的兄弟节点是同一个颜色,那么即使将它们同时修改为黑色,也不会破坏性质5。但是如果父亲的兄弟节点为黑色,即和父亲节点的颜色不一样,那么就一定会破坏性质5。(这里可以思考下为什么,不难理解,因为如果父亲是红色,父亲的兄弟是黑色,那么如果直接把父亲改成黑色,祖父改成红色,祖父到父亲子树上的叶子节点所经过的黑色节点就会比到父亲兄弟子树上的叶子节点多一个)

第二个问题:情况3是如何处理的,情况3:当前节点为红色(是父亲的左孩子),父亲为红色,父亲的兄弟为黑色,祖父为黑色。那么通过将父亲改成黑色,祖父改成红色,然后对祖父进行右旋,就可以得到一个正常的红黑树

第三个问题:情况4为什么需要变成情况3进行处理。情况4如果直接对父亲节点进行右旋,可能会破坏性质5。如果把父亲改成黑色,祖父改成红色,然后对祖父进行右旋,无法满足性质2。因此最为简便的方法就是直接对父亲进行左旋,把插入节点换到一条斜线上,变成情况3,然后进行处理。

删除操作:
红黑树的删除操作也大致上和二叉搜索树的删除类似,不过红黑树需要对被删除的节点的颜色进行判断,以选择是否需要进行修正操作,因此红黑树的删除需要记录被删除节点。
如果被删除节点的颜色是黑色,那么就需要进行修正操作。

原因很简单:如果被删除节点是红色,那么可以知道它的孩子都是黑色,它的父亲也是黑色,那么不管它父亲的新孩子是什么颜色都不会破坏红黑树的性质,因此不需要进行修正。
如果被删除节点是黑色,那么它的父亲可能是红色也可能是黑色,那么万一它父亲的新孩子是红色,就有可能会破坏红黑树的性质,因此需要进行修正。

在修正之前要意识到一个问题:因为被删除了一个黑色节点,因此从被删除节点的父亲节点的角度来看,它到左子树的后代叶子节点路径上就会少了一个黑色节点
删除的修正也分为多种情况(假设被删除节点是左孩子):
目前还没弄明白。准备后面再看看。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值