1. 前言
因为最近在研究计算几何1,里面都很多算法都依赖BBST(平衡搜索二叉树),而且需要对这个数据结构进行适配,所以Java里面内置的BBST已经不能很好的满足需求,所以趁这个机会详解一下BBST的一种实现:红黑树。
红黑树的理念和实现应该算是众多数据结构里面非常难的一种了,虽然网上也有很多教程解析了红黑树,但是看完总感觉还是没有完全理解。所以这个系列的文章,我会结合《算法 第四版》2上面的教程,完全从头开始详解红黑树的思路和实现。
另外,博主已经自己根据教材的代码,自己实现了红黑树,并添加了很多注释,所以讲解的过程中会结合自己的代码来进行说明,加深大家的印象。希望能通过这个系列的讲解,让对红黑树非常恐惧的童鞋,能完全掌握红黑树的设计理念。
综上,本系列的讲解安排如下:
第一部分:二叉查找树
第二部分:2-3查找树
第三部分:红黑树
第四部分:二叉查找树和红黑树的性能比较
2. 基本概念
首先,对于二叉树的定义, 想必大家都已经非常熟悉了,如果有不太清楚的童鞋,可以看看下面的图2理解一下:
注意:后续空连接默认省略,不会再特意标出。
接下来,我们来看看二叉查找树(后续简称BST)的定义:
一课二叉查找树(BST)是一颗二叉树,其中每个节点都含有一个Comparable的键(以及相关联的值)且每个节点的键都大于其左子树的任意节点的键,而小于右子树的任意节点的键。
这个定义看着太绕口了,我们假设键等于值,简单来说,BST是:
任意一个左子节点都小于它的父节点,任意一个右子节点都大于它的父节点。
比如下面这张图2就表示了一个标准的BST:
注意:每个节点(圆形)里面的内容表示键(Key,一般是字母),节点旁边对应的数值表示该Key对应的值(Value),同时也是该字母在ASCII中对应的数值,后续的图例都会这样表示,方便大家查找各个节点之间的大小顺序。
通过这个图例,我们可以看到BST中,左子树的Key一定小于相应的父节点,比如A和C都小于他们的父节点E。相似地,右子树的Key一定大于相应的父节点,比如R和H都大于他们的父节点E。这是BST必有的性质,也是一般二叉树没有的性质。
也许聪明的童鞋会又这样的疑问?如果BST中只存储一个Key,也就是Key和Value合二为一,那么是否能允许相同的节点?如果可以,我们应该如何表示这样的BST?
答案是肯定的,有两种方案可以解决重复节点的问题:
1)规定相同的节点要么存储在左子节点,要么存储在右子节点;
2)每个节点有个计数器(count),用来统计相同节点的个数,如果该节点已经存在,那么count++,如果是删除,count–,如果这时count == 0,则删除该节点。
3. 特性
根据BST的定义,那么整棵树是一个有序集合,而且我们知道,如果我们使用树的中序遍历(Inorder Traversal)去遍历整个BST,那么我们将得到一个有序(升序)的序列。关于树的中序遍历,大家可以参考这篇文章哒:二叉树的中序遍历
同样,我们如果把BST的每个节点投射到x轴上,那么整个投射关系也是呈现升序的关系:2
当然,表示同一序列的BST并不唯一,比如下面这个BST2也能表示和上面一样的有序序列:
并且,以某个点为参照,x轴正方向上,其最近的的投射点为其后继节点(Successor),x轴负方向上,其最近的投射点为其前继节点(Predecessor)。比如上图中,E的Successor为R,Predecessor为C。这里的前后继节点,我们将用于BST和红黑树的删除操作,如果没有他们,删除操作将会非常复杂。
接下来,我们将讲解BST的API的思路和实现:大小 - size(),查找 - get() 和 插入 - put()。
下一节:二叉查找树(二):大小和查找
系列汇总:超详细!红黑树详解文章汇总(含代码)
4 特别感谢
- 感谢 @SENNICHEN 制作系列文章封面图
5. 免责声明
※ 本文之中如有错误和不准确的地方,欢迎大家指正哒~
※ 此项目仅用于学习交流,请不要用于任何形式的商用用途,谢谢呢;