文章目录
1.基本概念
1.1. 2-3树引进
二叉搜索树在最好的情况下搜索的时间复杂度为 O(logn) ,但如果插入节点时,插入元素序列本身就是有序的,那么BST树就退化成一个线性表了,搜索的时间复杂度为 O(n)。
如果想要减少比较次数,就需要降低树的高度。在插入和删除节点时,要保证插入节点后不能使叶子节点之间的深度之差大于 1,这样就能保证整棵树的深度最小,这就是AVL 树解决 BST 搜索性能降低的策略。
但由于每次插入或删除节点后,都可能会破坏 AVL 的平衡,而要动态保证 AVL 的平衡需要很多操作,这些操作会影响整个数据结构的性能,除非是在树的结构变化特别少的情形下,否则 AVL 树平衡带来的搜索性能提升有可能还不足为了平衡树所带来的性能损耗。
2-3树是为了解决平衡二叉树插入删除的过程中为了维护平衡因子而消耗大量计算资源的问题
1.2. 2-3树定义
2-3 树要么为空要么具有以下性质:
- 对于 2节点,和普通的 BST 节点一样,有一个数据域和两个子节点指针,两个子节点要么为空,要么也是一个2-3树,当前节点的数据的值要大于左子树中所有节点的数据,要小于右子树中所有节点的数据
- 对于3节点,有两个数据域 a 和 b 和三个子节点指针,左子树中所有的节点数据要小于a,中子树中所有节点数据要大于 a 而小于 b ,右子树中所有节点数据要大于 b
对于一棵2-3树而言,树中节点可以分为2节点和3节点。这里的节点是指该节点的儿子节点的个数!
1.3. 2-3举例
如上图所示就是一棵2-3树。
在这棵树中既有2节点又有3节点!
2.2-3树的性质
- 对于每一个结点有 1 或者 2 个关键码键值
- 当节点有一个关键码的时,节点有 2 个子树
- 当节点有 2 个关键码时,节点有 3 个子树
- 所有叶子点都在树的同一层
- 在树种没有相同的值
上图就是一颗典型的2-3树。
3.2-3树的操作
3.1.数的遍历
2-3树的查找类似二分搜索树的查找,根据元素的大小来决定查找的方向。要判断一个元素是否存在,我们先将待查找元素和根节点比较,如果它和其中任意一个相等,那查找命中,否则根据比较的结果来选择查找的方向。
2-3树的遍历和二叉搜索树的遍历是相同的。
在查找的过程中,只有递推,没有回溯!
也就是说终止条件为NULL。如果向下递归的过程中找到了NULL,那么就说明没有命中,待查找的树中并没有待查找的元素。
3.2.树的插入
插入元素首先进行查找命中,若查找命中则不予插入此元素,如果需要支持重复的元素则将这个元素对象添加一个属性count。若查找未命中,则在叶子节点中插入这个元素。
PS:但就我自己的经验来看,其实是可以支持重复的元素的,重复的就并列排就可以了!这个可以看下我的二叉搜索树插入的实现算法。
3.2.1.向2-节点中插入元素;
如果未命中查找结束于2-节点,直接将2-节点替换为3-节点,并将待插入元素添加到其中。
3.2.2.向一颗只含有一个3-节点的树中插入元素;
如果命中查找结束于3-节点,先临时将其成为4-节点,把待插入元素添加到其中,然后将4-节点转化为3个2-节点,中间的节点成为左右节点的父节点。
如果之前临时4-节点有父节点,就会变成向一个父节点为2-节点的3-节点中插入元素,中间节点与父节点为2-节点的合并。
3.2.3.向一个父节点为2-节点的3-节点中插入元素;
插入元素后一直向上分解临时的4-节点,直到遇到2-节点的父节点变成3-节点不再分解。如果达到树根节点还是4-节点,则进行分解根节点,此时树高+1(只有分解根节点才会增加树高),下面动画2-3树插入会出这个例子。
3.2.4.向一个父节点为3-节点的3-节点中插入元素。
3.3.树的删除
我觉得2-3树的删除和AVL数的删除和二叉搜索树的删除基本思想是一样的,删除的过程我觉得是非常难的,树的删除应该是树的操作中最难的一部分,它需要考虑到各个方面,但是如果你对二叉搜索树的删除比较了解,知道它的实现过程,至于其它树的删除就不需要太过于纠结代码具体的实现。
在这篇博客中已经实现了,大家可以取看看。
总结
2-3树的出现目的是为了解决平衡二叉树插入删除的过程中为了维护平衡因子而消耗大量计算资源的问题,大家只需要对这个变种树在概念上有一个了解就即刻,不需要把所有的代码全部实现,对二叉搜索树实现就可以了!