注意:以下均用2-3树指代2-3查找树
2-3树的定义:
2-3树结点定义:
templateclass tree_23_node //2-3树的结点 { public: tree_23_node(); public: int m_type; //用于记录结点的类型,如果是2结点,那么m_type=2,如果是3结点,m_type=3,否则m_type=0; T* m_small_data; T* m_large_data; class tree_23_node *m_parent; //父结点 class tree_23_node *m_l_child; //左孩子 class tree_23_node *m_m_child; //中孩子 class tree_23_node *m_r_child; //右孩子 } template class tree_23 //2-3树 { public: tree_23(); public: class tree_23_node *m_root; }
上图是一颗简单的2-3树,2-3树中序遍历的顺序为:左孩子->当前结点小值->(中结点->当前结点大值)->右孩子,
其中序遍历结果是:8 10 12 14 16 20 24 26 28 32 34 38
查找:
2-3树的查找操作和二叉树的查找操作是一样的,
插入:
注意:2-3树的长高发生在根结点,而不是叶子结点
普通二叉树的插入操作:假设插入的值为data,
2-3树的插入分析:假设插入的值为data,
2-3树的插入操作:假设插入的值为data,
情况2.1:leaf是2结点,直接向leaf插入data
情况2.2:leaf是3结点,但是leaf的parent是2结点,(注意这里的leaf的孩子是NULL空指针,没有画出来)
情况2.2分析:
由于leaf是3结点,如果直接将9插入到leaf中,那么leaf会变成4结点(leaf会有3个值,4个孩子),不符合2-3树的性质;
解决办法:
如下面的3张图,在创建 新的键值data和新的孩子new_node 时,要考虑数值之间的大小关系,详情见下面的图,并且data和new_node在和parent组成3结点时也要考虑清楚各种情况,总之很繁琐
情况2.3:leaf是3结点,但是leaf的parent也是3结点,(注意这里的leaf的孩子是NULL空指针,没有画出来)
情况2.4:leaf是3结点,但是leaf的parent也是3结点,并且parent是根结点
2-3树中插入的注意事项
1、当前结点分裂形成新的当前结点、新建结点、新值时,一定不能丢失所有的孩子信息,其中当前结点有3个孩子,以及上传的一个孩子(是什么呢?它是前一个新建结点,可以是NULL),共4个孩子,可以形成两个2结点
2、如果是根结点的分裂,要将根结点的m_root的指向及时更新
3、2-3树的长高只能通过根结点的分裂
删除:
注意:2-3树的逆生长(即高度降低)一定使发生在根结点,而不是叶子结点
普通二叉树的删除操作:假设删除的值为data
2-3树的删除操作:假设删除的值为data
下面我将通过图示来说明:待删除2-3树如下
删除操作----情况1:如果待删除结点不是叶子结点,将问题转化为删除叶子结点
删除操作----情况2:如果待删除结点是叶子结点
情况2.1:删除结点是3结点,直接删除data,删除结点变为2结点,然后return
情况2.2:删除结点是2结点,直接删除data,删除结点变为0结点
情况2.2.1:如果父兄至少有一个3结点,从父结点或者兄弟结点借值后组建新的删除结点(变为2结点),然后return
这种情况是最繁琐的,要分父兄都是2结点、父是2结点兄是3结点、父是3结点兄是2结点这3种情况考虑,还要考虑delete是在父结点的左结点、结点还是右结点,但是具体的操作是一样的,太繁琐了就不具体讨论了,本人共考虑了16小情况,还没有找到简单的解法
情况2.2.2:如果父兄都是2结点,此时待删除结点是0结点,需要递归处理
如果说情况2.2.1是繁琐,那么情况2.2.2就很简洁了,这里我们定义以下0结点,0结点代表结点没有键值,但是不代表没有孩子(注意NULL也视为有一个小孩),什么意思呢?看下面的图示
2-3树中删除的注意事项
1、通过将delete结点从叶子结点向根结点移动,不断地判断delete结点的父兄结点符合哪种状况,如此递归下去,直到找到问题的出口并return,
2、注意当delete的父结点是NULL,那么树逆生长(树的高度-1),这就是2-3树的逆生长(即高度降低)一定使发生在根结点,而不是叶子结点
3、在结点的融合过程中,时刻注意更新每个结点的信息,包括结点类型,结点值,结点的父结点以及子结点
结尾:
作为最简单的B树,2-3树的实现仍然很繁琐,2-3-4树就更加麻烦了,且创建一颗2-3(-4)树的效率可能不尽如人意,而且红黑树作为2-3(-4)树的变种,红黑树的插入和删除更高效快捷,那么理解并且用代码实现2-3树似乎不重要了,但是理解2-3树对理解红黑树是有很大的帮助的,红黑树中的插入情况可以和2-3树中的插入、删除情况有所区别,但区别不大。
本人的2-3树代码太冗长了,就不发出来了。