通过引入结点度大于2的查找树,可以得到一种插入算法和删除算法都比AVL树简单的树结构,且这些算法的时间复杂性是O(logn)。这种树结构称为2-3树。2-3树的名字反映了2-3树具有如下性质:一棵2-3树中每个内部结点的度或者是2,或者是3。其中度为2的结点称为2结点,度为3的结点称为3结点。
定义:一棵2-3树(2-3 Tree)是一棵查找树,该查找树或者为空,或者满足如下性质:
1,每个内部结点或者是一个2结点,或者是一个3结点。一个2结点存放一个元素,而一个3结点存放两个元素。
2,令lchild和mchild表示2结点的两个儿子,datal表示该节点存放的元素,且datal.key是该元素的关键字。以lchild为根的子树中所有结点的关键字都小于datal.key,而以mchild为根的子树中所有结点的关键字都大于datal.key。
3,零lchild,mchild和rchild表示3结点的三个儿子,datal和datar表示该节点存放的左右两个元素,且datal.key < datar.key。以lchild为根的子树的所有结点的关键字都小于datal.key,以mchild为根的子树的所有结点的关键子都大于datal.key而小于datar.key,而以rchild为根的子树中所有结点的关键字都大于datar.key。
4,所有外部结点位于同一层。
2-3树的查询操作不用细说,实现比较容易,重点在于2-3树的插入和删除操作,2-3树的插入操作涉及结点拆分,删除操作涉及数据的转移(或者称为旋转吧)以及结点合并(三种情况的旋转以及三种情况的合并,详细见REF[1]的2-3树章节)等等操作。
具体的插入和删除的原理可以可以参考文章《2-3树删除和插入操作的小结》,后面的代码主要是对REF[1]中的算法的实现:
代码:
#include <iostream>
#include <stack>
using namespace std;
typedef struct Tree23Node * Tree23;
typedef struct Tree23Node {
int datal;
int datar;
Tree23 lchild,mchild,rchild;
} Tree23Node;
#define INT_MAX 0x3f3f3f3f
stack<Tree23> s;
int compare(int x, Tree23 t)
{
if (x < t->datal)
return 1;
else if (x > t->datar)
return 3;
else if ( x < t->datar && x > t->datal)
return 2;
else
return 4;
}
Tree23 createNode(int key)
{
Tree23 t = new Tree23Node;
t->datal = key;
t->datar = INT_MAX;
t->lchild = t->mchild = t->rchild = NULL;
return t;
}
void newRoot(Tree23 *root,int key,Tree23 midSub)
{
Tree23 t = createNode(key);
t->lchild = *root;
t->mchild = midSub;
*root = t;
}
bool isleaf(Tree23 root)
{
if (root && root->datal < INT_MAX && root->lchild == NULL &&
root->mchild == NULL && root->rchild == NULL)
return true;
return false;
}
Tree23 findNode(Tree23 root, int key)
{
Tree23 t = NULL;
while (root){
if (!isleaf(root))
s.push(root);
if (isleaf(root))
t = root;
switch (compare(key,root)) {
case 1: root = root->lchild;
break;
case 2: root = root->mchild;
break;
case 3: root = root->rchild;
break;
case 4: return NULL;
}
}
return t;
}
void put(Tree23 *root, int key,Tree2