Alex_McAvoy

想要成为渔夫的猎手

2-3 树

【结构】

2-3 树(2-3 Tree),是一种多路查找树,其是一棵具有如下特性的树:

  • 每个结点都具有 $2$ 个孩子或 $3$ 个孩子,具有 $2$ 个孩子的结点称为 $2$ 结点,具有 $3$ 个孩子的结点称为 $3$ 结点
  • $2$ 结点包含 $1$ 个关键码,且其具有 $2$ 个孩子,同时,左子树包含小于 $2$ 结点的元素,右子树包含大于 $2$ 结点的元素
  • $3$ 结点包含一大一小 $2$ 个关键码,且其具有 $3$ 个孩子,同时,左子树包含小于 $3$ 结点两个关键码的元素,右子树包含大于 $3$ 结点两个关键码的元素,中间子树包含介于 $3$ 结点两个关键码之间的元素
  • 所有叶结点都在同一层

当所有叶结点均处于同一层时,就说这棵树是树高平衡的,可见,2-3 树是树高平衡的,其最大的优点正是能够以相对较低的代价来保持树高平衡

如下图,内部结点 $12$、$48$ 为 $2$ 结点,内部结点 $\{18,33\}$、$\{23,30\}$ 为 $3$ 结点

同时,可以发现:

  • 对于一棵高度为 $h$ 的 2-3 树,其至少有 $2^{h-1}$ 个叶结点,此时每个分支结点都有 $2$ 个孩子,从而形成一棵满二叉树
  • 对于一棵高度为 $h$ 的 2-3 树,其至多有 $3^{h-1}$ 个叶结点,此时每个分支结点都有 $3$ 个孩子,从而形成一棵满三叉树

【查找】

在 2-3 树中,查找一个关键码的过程类似于在二叉排序树中的查找

在 2-3 树中查找给定 $k$ 值的过程是:

  1. root 是空树,查找失败
  2. k = root->data,查找成功
  3. 对于 $2$ 结点,若 k < root->data,则在 root 的左子树进行查找;若 k > root->data,则在 root 的右子树进行查找
  4. 对于 $3$ 结点,若 k < root->smallData,则在 root 的左子树进行查找;若 k > root->bigData,则在 root 的右子树进行查找;否则,在 root 的中间子树进行查找

上述过程一直持续到 $k$ 被找到或者待查找的子树为空,若待查找的子树为空,则查找失败

值得注意的是,当查找失败时,恰好找到了以 $k$ 为键值的新结点在 2-3 树中的插入位置

以下图为例,要如果查找 $24$,首先查找根结点,由于 $24$ 大于根结点的 smallData=18,小于 bigData=33,那么进入第二层的中间子树查找;在第二层中,由于 $24$ 大于 smallData=23,小于 bigData=30,进入第三层的中间子树查找,最终到达包含 $24$ 的结点

【插入】

在 2-3 树中,插入一个关键码的过程类似于在二叉排序树中的插入

插入一个记录的过程如下:

1.找到要被插入记录应该被插入的叶结点

2.若该叶结点为一个 $2$ 结点,那么可以直接将该记录添加在该叶结点中,从而升级为 $3$ 结点

3.若该叶结点为一个 $3$ 结点,那么需要将该结点进行分裂-提升,拆分为两个 $2$ 结点,再向上提升,具体步骤如下:

1)设要插入的叶结点为 $L$,分裂一个新结点 $L’$

2)结点 $L$ 得到 $3$ 结点中两个关键码与新插入的记录中最小的一个,结点 $L’$ 得到最大的一个

3)进行一次提升,即将剩余的一个关键码与一个指向 $L’$ 的指针传回父结点,并将被提升的关键码插入父结点

4)若父结点为 $2$ 结点,那么重复步骤 2

5)若父结点为 $3$ 结点,那么重复步骤 3 的分裂-提升过程


如下图,假设在当前 2-3 树中,依次插入记录 $14$、$55$

在插入 $14$ 时,从根结点开始查找,到达存储 $15$ 的叶结点时,发现其是一个 $2$ 结点,直接将 $14$ 插入即可

在插入 $55$ 时,从根结点开始查找,到达存储 $\{50,52\}$ 的结点,其是一个 $3$ 结点,需要进行分裂-提升

此时,存储 $\{50,52\}$ 的结点保存 $50$、$52$、$55$ 中最小的 $50$,新分裂的结点保存最大的 $55$,剩余的 $52$ 向上提升,发现其存储 $48$ 的结点为 $2$ 结点,直接将 $52$ 插入

【删除】

当从 2-3 树删除一个关键码时,有三种情况要考虑:

1.从内部结点删除记录

当所删除的关键码为内部结点时,对树进行中序遍历,将要删除的关键码的中序前驱或中序后继元素作为替代元素即可

2.从叶结点为 $3$ 结点删除记录

当所删除的关键码位于叶结点且该结点为 $3$ 结点时,直接在该结点删除该关键码即可,不会影响整棵树的结构

3.从叶结点为 $2$ 结点删除记录

当所删除的关键码位于叶结点且该结点为 $2$ 结点时,可分为四种情况进行讨论

1)$2$ 结点的父结点是 $3$ 结点

此时,将 $3$ 结点进行拆分,根据删除要删除的元素的大小,将被拆分的点与 $3$ 结点的子树进行合并

2)$2$ 结点的父结点是 $2$ 结点,且拥有一个 $3$ 结点的右孩子

此时,将父结点与右孩子一起进行左旋,令父结点的右孩子的较小值成为新的父结点,原先的父结点成为新的左孩子,原先的右孩子的较大值不做改变

3)$2$ 结点的父结点是 $2$ 结点,且拥有一个 $2$ 结点的右孩子

此时,在不破坏 2-3 树的性质的前提下,将 $2$ 结点的右孩子变为 $3$ 结点的右孩子,然后删除要删除的元素,并将剩下的三个点进行左旋调整

4)当前树是一个满二叉树

此时,删除任意一个结点都会破坏 2-3 树的性质,需要考虑在不改变 2-3 树的顺序的情况下,将 2-3 树的层次减少一层

感谢您对我的支持,让我继续努力分享有用的技术与知识点!