二叉搜索树的高度不确定,可能导致低查询效率,AVL尽管树相对平衡但是插入删除带来的较高的调整复杂度。2-3树尝试通过绝对平衡来寻找突破口,实际上是它具有较高的插入复杂度和实现复杂度,实际应用并不广泛。 但2-3树衍生的红黑树在很多底层源码中都有应用。
一颗2-3查找树或一颗空树由以下结点组成:
2-结点,包含一个键,以及左右两个链接,分别指向的2-3树的键都小于/大于该结点,与二叉搜索树的普通结点相同。3-结点,包含两个键,和三个链接,左边的键小于右边的键,左链接指向的树(结点)小于左键,中间链接指向的树大于左键并且小于右键。右链接指向的树大于右键。2-3树的叶子结到根结点的距离相同,也就是说它平衡的。如下例子:
查找操作和二叉搜索树类似,从根结点开始比较,只不过遇到3-结点需要做两次比较来找到合适的链接。
和二叉搜索树相同,2-3树的元素插入也需要找到合适的叶子结点,只不过二叉搜索树讲新的结点挂在了叶子结点的下面,而2-3树则需要和叶子结点进行融合,然后在进行调整操作。 所谓的融合说白了就是将2-结点变为3-结点,3-结点变为4-结点。
4-结点:插入时用来调整平衡的临时结点,它含有三个键(及其对应的值)和四条链接,四条链接代表了从小到大的四个范围,这四个范围用三个键切分开。
因此也就有插入的元素查找到的叶子结点为2-结点和3-结点两种情况,定义中描述只能有2-结点和3-结点存在,为什么会出现4-结点呢?不要急继续往下看。
当查找未命中结束于2-结点处时情况比较简单,比如插入3元素,我们找到了键值为4的结点,只需将2-结点变为3-结点即可。
当查找未命中结束于3-结点处时,如果我们将键值映射融合到其中就会变成一个4-结点。 形成的4-结点在2-3树中是不允许的,所以要进行分解。将这个4-结点(小、中、大)分解成两个2-子结点(小和大)和一个它们的父结点(中),但是这又增加了树的高度,使得整个树不平衡, 因此还需要将中键结点融入到父结点中,如果父结点为2-结点,那么直接融入,如果父结点为3-结点,先暂时融入形成4-结点,直到融合到一个2-结点或者将根结点也分解了,这个时候是整棵树的高度提升。
再来看一个稍微复杂的一个例子:
如果根结点变为了4-结点,需要分解为3个2-结点,使得树的高度增加。同时也保持了树的平衡。 通过插入结点和标准的二叉查找树由上向下生长不同,2-3树的生长是由下向上的。
删除操作再很多书中都没有介绍,笔者查阅了大量的相关博客文档,了解了一点2-3树的删除操作,其过程虽然情况复杂,但都是有规律可循。
删除键可以分为两种
键在叶子结点键在非叶子结点删除叶子结点中的键又可以分为两类,叶子结点是3-结点和2-结点,前者比较容易,直接去键,变为2-结点。如果键在2-结点中将结点删除,此时树肯定不满足2-3树的性质,这时候需要根据 其父结点和兄弟结点的情况来决定,
父结点为2-结点,兄弟结点为3-结点,将兄弟结点拆分,和父结点形成高度为2的二叉树,见下图中(1)。父结点为2-结点,兄弟结点为2-结点,将父结点与兄弟结点合并,将合并后的结点看成当前结点,后续的操作需要判断父结点和兄弟结点,重复从1开始判断。父结点为3-结点,可以分为两类: (1).兄弟结点不全是3-结点,意思是两个兄弟结点至少有一个2-结点,这种情况向下移动一个父键与2-结点的兄弟融合形成3-结点,见(3.1)。 (2).兄弟结点全是3-结点,意思是除了删除的键在2-结点中,其他兄弟结点都是3-结点,这种情况向下移动一个父键,向上移动一个兄弟键。图中(3.2), 删除非叶子结点中键,使用中序遍历找到待删除键的后继键,然后将后继节键移到删除键的位置,此时就将问题转化为删除结点为叶子结点2-3-4树只是在2-3树的基础上进行了扩展。2-3-4树的优势在于能尽量存放多的数据元素,而且树的高度还能尽量地低。不好的地方就是由于节点类型的多样,导致操作起来更加复杂。具有如下性质:
任一节点只能是1、2、3个key,对应的子节点为2个子节点或3个子节点或4个子节点;所有叶子节点到根节点的长度一致;每个节点的key从左到右保持了从小到大的顺序,两个key之间的子树中所有的key一定大于它的父节点的左key,小于父节点的右key,对于3个key的节点,两两key之间也是如此。2-3-4树的查询、插入和删除和2-3树基本一样,需要明确的一点是2-3树也属于2-3-4树,因为2-3树满足2-3-4的所有性质。
和标准的二叉查找树由上向下生长不同,2-3树的生长是由下向上的。和标准的二叉查找树由上向下生长不同,2-3树的生长是由下向上的。2-3 树在最坏的情况下仍有较好的性能。每个操作中处理每个节点的时间都不会超过一个很小的常数,且这两个操作都只会访问一条路径上的节点,所以任何查找或者插入的成本都不会超过对数级别。
其缺点是实现困难,主要表现在对不同结点的状态的表示、树的平衡维护、删除的复杂度较高,实际应用场景也很少,但是可以通过一种跟容易实现的方式红黑树来表示代替它,学习2-3树也有助我们更好的去理解红黑树。