前篇文章(2-3树的实现分析)已经分析了2-3树的遍历(traverse),检索(retrieve),插入(insert)和删除(delete)的算法,经过三天的努力,我在分析的基础上采用回溯的方式实现了2-3树,由于还在初级阶段,肯定会出现不少的问题,请多赐教。但经过测试,运行还算正确。下面是代码实现:
2-3树的异常处理类
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// two_three_tree_exception.h
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#ifndef TWO_THREE_TREE_EXCEPTION_H_
#define TWO_THREE_TREE_EXCEPTION_H_
#include<stdexcept>
#include<string>
class TTTException :public std::logic_error
{
public:
TTTException(const std::string &message="")
:std::logic_error(message.c_str())
{}
};
#endif
2-3树的item类,每个节点的数据的存储在这里,包含了一个关键字keyword和相关的值 tel。
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// two_three_tree_item.h
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#ifndef TWO_THREE_TREE_ITEM_H_
#define TWO_THREE_TREE_ITEM_H_
#include<string>
typedef std::string KeyType;
class ItemType
{
public:
ItemType(){}
ItemType(const KeyType &_key,const std::string &_tel)
:keyword(_key),tel(_tel)
{}
//get key word keyword;
const KeyType & getKey()const
{
return keyword;
}
//set value tel;
void setTel(const std::string &_tel)
{
tel=_tel;
}
//get value tel;
std::string getTel()const
{
return tel;
}
private:
KeyType keyword;
std::string tel;
};
#endif
2-3树的节点类,实现树的完整连接,其中包含两个item,三个孩子:左孩子,中孩子,右孩子
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// two_three_tree_node.h
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#ifndef TWO_THREE_TREE_NODE_H_
#define TWO_THREE_TREE_NODE_H_
#include"two_three_tree_item.h"
class Node
{
private:
Node(ItemType &_item,Node *_leftPtr,Node*_middlePtr)
:fItem(_item),LeftPtr(_leftPtr)
,MiddlePtr(_middlePtr),RightPtr(NULL)
{Nitems=1;}
//当节点中的item项数为1时,调用此函数重新整合节点
void reSet(ItemType &_item,Node *_leftPtr,
Node*_middlePtr)
{
Node *tempL=_leftPtr,*tempM=_middlePtr;
fItem=_item;
LeftPtr=tempL;
MiddlePtr=tempM;
RightPtr=NULL;
setNumb(1);
}
//当节点中的item项数为2时,调用此函数重新整合节点
void reSet( ItemType &_fitem,ItemType &_litem,Node *_leftPtr,Node *_middlePtr,Node *_rightPtr)
{
ItemType tempfi=_fitem,templi=_litem;
Node *tempL=_leftPtr,*tempM=_middlePtr,*tempR=_rightPtr;
LeftPtr=tempL;
MiddlePtr=tempM;
RightPtr=tempR;
fItem=tempfi;
lItem=templi;
setNumb(2);
}
//设置节点中item的数目
void setNumb(int n)
{
Nitems=n;
}
//清空当前节点中的孩子和item(items)
void clear()
{
LeftPtr=MiddlePtr=RightPtr=NULL;
setNumb(0);
}
ItemType fItem,lItem;
Node *LeftPtr,*MiddlePtr,*RightPtr;
int Nitems;
friend class ttTree;
};
#endif
2-3树实现的头文件
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// two_three_tree.h
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#ifndef TWO_THREE_TREE_H_
#define TWO_THREE_TREE_H_
#include"two_three_tree_exception.h"
#include"two_three_tree_node.h"
typedef void (*FuncT)(ItemType &_item);
class ttTree
{
public:
//默认构造函数
ttTree();
//复制构造函数
ttTree(ttTree &_tree);
//析构函数
virtual ~ttTree();
virtual bool empty()const;
//检索
virtual void retrieve(const KeyType &_keyword,ItemType &_item)throw(TTTException);
//遍历
virtual void traverse(FuncT visit);
//插入
virtual void insert(const ItemType & newitem)
throw(TTTException);
//删除
virtual void remove(const KeyType &keyword)
throw(TTTException);
//赋值操作符重载
virtual ttTree &operator=(ttTree &oldtree)throw(TTTException);
protected:
bool insertTree(Node *&treePtr,Node *&tempPtr,ItemType &tempItem)throw(TTTException);
bool removeTree(Node *&treePtr,Node *&tempPtr,const KeyType &_key)
throw(TTTException);
void retrieveTree(Node * &treePtr,const KeyType &_key,ItemType &_item)throw(TTTException);
void traverseTree(Node *&treePtr,FuncT visit);
//复制构造函数
void copyTree(Node *&newtree, Node* &oldtree)throw(TTTException);
void destroy(Node *&treePtr);
//在插入的情况下,当当前的item数大于2时,调用此函数维护2-3的原有属性
bool insertMaintain(Node *&treePtr,Node *&tempPtr,ItemType &tempItem,char ch)throw(TTTException);
//在删除的情况下,当当前的item数小于1时,调用此函数维护2-3的原有属性
bool removeMaintain(Node *&treePtr,Node *&tempPtr,char ch);
//寻找当前节点treeptr中项目的中序后继,
int processLeft(Node *&treePtr,Node*&tempPtr,ItemType &replace);
//当前的节点treePtr中item数目为2时返回true,否侧返回false
bool twoItem(Node *&treePtr)const;
//当前的节点treePtr中的item数目为1时返回true,否则返回false
bool oneItem(Node *&treePtr)const;
//判断当前节点treePtr是否为根节点,如是则返回true,否则返回false
bool isRoot(Node *&treePtr)const;
//判断当前节点treePtr是否为叶节点,若是则返回true,否则返回false
bool isLeaf(Node *&treePtr)const;
//判断当前节点treePtr中是否存在与关键字_key相等的项
//若存在则返回true,否则返回false
bool equal(Node*&treePtr,const KeyType & _key)const;
//按项中关键字的大小重新排序first,middle,last,使其满足
//first.getkey()<=middle.getKey()<=last.getkey()
void sort(ItemType &first,ItemType &middle,ItemType &last);
private:
Node *root;
};
#endif
2-3树的现实文件
#include<cstddef>
#include<cassert>
#include"two_three_tree.h"
using namespace std;
ttTree::ttTree():root(NULL)
{
}
ttTree::ttTree(ttTree &_tree)
{
copyTree(root,_tree.root);
}
ttTree::~ttTree()
{
destroy(root);
}
bool ttTree::empty()const
{
return root==NULL;
}
void ttTree::retrieve(const KeyType &_keyword,ItemType &_item)
throw(TTTException)
{
try{
retrieveTree(root,_keyword,_item);
}catch(TTTException &e)
{
throw;
}
}
void ttTree::traverse(FuncT visit)
{
traverseTree(root,visit);
}
void ttTree::insert(const ItemType &newitem)
throw(TTTException)
{
Node *inPtr=NULL;
//point to a new creating Node, and deliver new node to
//last time insertTree function called
ItemType inItem=newitem;
// contain the "internal" item value that canot be cotained in this
// node
try{
bool ok=insertTree(root,inPtr,inItem);
if(!ok)
throw TTTException("TTTException :insert newitem failed !");
}catch(TTTException &e)
{
throw;
}
}
void ttTree::remove(const KeyType &keyword)
throw(TTTException)
{
Node *rePtr=NULL;
//point to the empty node's child
try{
bool ok=removeTree(root,rePtr,keyword);
if(!ok)
throw TTTException("TTTException :remove item failed !");
}catch(TTTException &e)
{
throw;
}
}
ttTree &ttTree::operator=(ttTree &oldtree)throw(TTTException)
{
try{
if(this==&oldtree)
return *this;
else
{
destroy(root);
copyTree(root,oldtree.root);
}
}catch(TTTException &e)
{
throw;
}
}
bool ttTree::insertTree(Node *&treePtr,Node *&tempPtr,
ItemType &tempItem)throw(TTTException)
{
if(empty())
{
treePtr=new Node(tempItem,NULL,NULL);
if(treePtr==NULL)
throw TTTException("TTTException :allocate memory failed !");
return true;
}
else if(isLeaf(treePtr))
{
try{
return insertMaintain(treePtr,tempPtr,tempItem,'F');
}catch(TTTException &e)
{
throw;
}
}
else if((tempItem.getKey())<(treePtr->fItem.getKey()))
{
bool success=insertTree(treePtr->LeftPtr,tempPtr,tempItem);
if(success)
return true;
else
{ try{
return insertMaintain(treePtr,tempPtr,tempItem,'L');
}catch(TTTException &e)
{
throw;
}
}
}
else if(twoItem(treePtr)&&(tempItem.getKey()>=treePtr->lItem.getKey()))
{
bool success=insertTree(treePtr->RightPtr,tempPtr,tempItem);
if(success)
return true;
else
{
try{
return insertMaintain(treePtr,tempPtr,tempItem,'R');
}catch(TTTException &e)
{
throw;
}
}
}
else
{
bool success=insertTree(treePtr->MiddlePtr,tempPtr,tempItem);
if(success)
return true;
else
{
try{
return insertMaintain(treePtr,tempPtr,tempItem,'M');
}catch(TTTException &e)
{
throw;
}
}
}
}
bool ttTree::removeTree(Node *&treePtr,Node *&tempPtr,const KeyType &_key)
throw(TTTException)
{
if(empty())
throw TTTException("TTTException :two three tree is empty !");
else if(isLeaf(treePtr)&&(!equal(treePtr,_key)))
throw TTTException("TTTException :item contian keyword not exist !");
else if(equal(treePtr,_key))
{
ItemType replace;
if(treePtr->fItem.getKey()==_key)
{
int placeok=processLeft(treePtr->MiddlePtr,tempPtr,replace);
treePtr->fItem=replace;
if(placeok==0)
return true;
else if(placeok==1)
{
return removeMaintain(treePtr,tempPtr,'M');
}
else
{
if(twoItem(treePtr))
{
treePtr->reSet(treePtr->lItem,NULL,NULL);
return true;
}
return false;
}
}
else
{
int placeok=processLeft(treePtr->RightPtr,tempPtr,replace);
//must deal with solution that treePtr is a leaf
treePtr->lItem=replace;
if(placeok==0)
return true;
else if(placeok==1)
{
return removeMaintain(treePtr,tempPtr,'R');
}
else
{
treePtr->reSet(treePtr->fItem,NULL,NULL);
return true;
}
}
}
else if(_key<(treePtr->fItem.getKey()))
{
bool success=removeTree(treePtr->LeftPtr,tempPtr,_key);
if(success)
return true;
else
{
return removeMaintain(treePtr,tempPtr,'L');
}
}
else if(twoItem(treePtr)&&(_key>=(treePtr->lItem.getKey())))
{
bool success=removeTree(treePtr->RightPtr,tempPtr,_key);
if(success)
return true;
else
return removeMaintain(treePtr,tempPtr,'R');
}
else
{
bool success=removeTree(treePtr->MiddlePtr,tempPtr,_key);
if(success)
return true;
else
return removeMaintain(treePtr,tempPtr,'R');
}
}
int ttTree::processLeft(Node *&treePtr,Node *&tempPtr,ItemType &replace)
{
if(treePtr==NULL)
return 2;
else if(isLeaf(treePtr))
{
replace=treePtr->fItem;
if(twoItem(treePtr))
{
treePtr->reSet(treePtr->lItem,NULL,NULL);
return 0;
}
tempPtr=NULL;
return 1;
}
else
{
int result=processLeft(treePtr->LeftPtr,tempPtr,replace);
if(result==0)
return 0;
else if(result==1)
{
bool placeok=removeMaintain(treePtr,tempPtr,'L');
if(placeok)
return 0;
else
return 1;
}
}
}
void ttTree::retrieveTree(Node * &treePtr,const KeyType &_key,
ItemType &_item) throw(TTTException)
{
if(empty())
throw TTTException("TTTException:empty two three tree !");
else if(isLeaf(treePtr)&&(!equal(treePtr,_key)))
throw TTTException("TTTException :the item whose key word equal to _key not exist !");
else if(equal(treePtr,_key))
{
if(treePtr->fItem.getKey()==_key)
_item=treePtr->fItem;
else
_item=treePtr->lItem;
}
else if(_key<(treePtr->fItem.getKey()))
retrieveTree(treePtr->LeftPtr,_key,_item);
else if(twoItem(treePtr)&&(_key>=treePtr->lItem.getKey()))
retrieveTree(treePtr->RightPtr,_key,_item);
else
retrieveTree(treePtr->MiddlePtr,_key,_item);
}
void ttTree::traverseTree(Node *&treePtr,FuncT visit)
{
if(treePtr!=NULL)
{
traverseTree(treePtr->LeftPtr,visit);
visit(treePtr->fItem);
traverseTree(treePtr->MiddlePtr,visit);
if(twoItem(treePtr))
{
visit(treePtr->lItem);
traverseTree(treePtr->RightPtr,visit);
}
}
}
void ttTree::copyTree(Node *&newtree,Node * &oldtree)
throw(TTTException)
{
if(oldtree!=NULL)
{
newtree= new Node(oldtree->fItem,NULL,NULL);
if(newtree==NULL)
throw TTTException("TTTException :allocate memory failed !");
if(twoItem(oldtree))
{
newtree->reSet(oldtree->fItem,oldtree->lItem,
NULL,NULL,NULL);
}
copyTree(newtree->LeftPtr,oldtree->LeftPtr);
copyTree(newtree->MiddlePtr,oldtree->MiddlePtr);
if(twoItem(oldtree))
copyTree(newtree->RightPtr,oldtree->RightPtr);
}
}
void ttTree::destroy(Node *&treePtr)
{
if(treePtr!=NULL)
{
destroy(treePtr->LeftPtr);
destroy(treePtr->MiddlePtr);
if(twoItem(treePtr))//may be left out
destroy(treePtr->RightPtr);
delete treePtr;
treePtr=NULL;
}
}
bool ttTree::twoItem(Node *&treePtr)const
{
if(treePtr->Nitems==2)
return true;
else
return false;
}
bool ttTree::oneItem(Node *&treePtr)const
{
if(treePtr->Nitems==1)
return true;
else
return false;
}
bool ttTree::isRoot(Node *&treePtr)const
{
if((root!=NULL)&&(root==treePtr))
return true;
else
return false;
}
bool ttTree::isLeaf(Node *&treePtr)const
{
if((treePtr->LeftPtr==NULL)&&(treePtr->MiddlePtr==NULL)
&&(treePtr->RightPtr==NULL))
return true;
else
return false;
}
bool ttTree::equal(Node *&treePtr,const KeyType &_key)const
{
if(treePtr->fItem.getKey()==_key)
return true;
else if(twoItem(treePtr)&&(treePtr->lItem.getKey()==_key))
return true;
else
return false;
}
void ttTree::sort(ItemType &first,ItemType &middle,ItemType &last)
{
ItemType temp;
if(first.getKey()<middle.getKey())
{
if(first.getKey()<last.getKey())
{
if(middle.getKey()>=last.getKey())
{
temp=middle;
middle=last;
last=temp;
}
}
else
{
temp=last;
last=middle;
middle=first;
first=temp;
}
}
else
{
if(middle.getKey()<last.getKey())
{
if(first.getKey()<last.getKey())
{
temp=middle;
middle=first;
first=temp;
}
else
{
temp=middle;
middle=last;
last=first;
first=temp;
}
}
else
{
temp=last;
last=first;
first=temp;
}
}
}
bool ttTree::removeMaintain(Node *&treePtr,Node *&tempPtr,char ch)
{
if(ch=='R')//R for ptr1 relative to ptr2's position is right
{
if(twoItem(treePtr->MiddlePtr))
{
treePtr->RightPtr->reSet(treePtr->lItem,treePtr->MiddlePtr->RightPtr,tempPtr);
treePtr->reSet(treePtr->fItem,treePtr->MiddlePtr->lItem,treePtr->LeftPtr,treePtr->MiddlePtr,treePtr->RightPtr);
treePtr->MiddlePtr->reSet(treePtr->MiddlePtr->fItem,treePtr->MiddlePtr->LeftPtr,treePtr->MiddlePtr->MiddlePtr);
return true;
}
else
{
treePtr->MiddlePtr->reSet(treePtr->MiddlePtr->fItem,treePtr->lItem,treePtr->MiddlePtr->LeftPtr,treePtr->MiddlePtr->MiddlePtr,tempPtr);
treePtr->RightPtr->clear();//clear function ----
delete treePtr->RightPtr;
treePtr->RightPtr=NULL;
treePtr->reSet(treePtr->fItem,treePtr->LeftPtr,treePtr->MiddlePtr);
return true;
}
}
else if(ch=='M')
{
if(twoItem(treePtr->LeftPtr))// function ---------------
{
treePtr->MiddlePtr->reSet(treePtr->fItem,treePtr->LeftPtr->RightPtr,tempPtr);
treePtr->reSet(treePtr->LeftPtr->lItem,treePtr->lItem,treePtr->LeftPtr,treePtr->MiddlePtr,treePtr->RightPtr);
treePtr->LeftPtr->reSet(treePtr->LeftPtr->fItem,treePtr->LeftPtr->LeftPtr,treePtr->LeftPtr->MiddlePtr);
return true;
}
else
{
treePtr->LeftPtr->reSet(treePtr->LeftPtr->fItem,treePtr->fItem,treePtr->LeftPtr->LeftPtr,treePtr->LeftPtr->MiddlePtr,tempPtr);
treePtr->MiddlePtr->clear();
delete treePtr->MiddlePtr;
treePtr->MiddlePtr=NULL;
if(oneItem(treePtr))
{
treePtr->setNumb(0);
tempPtr=treePtr->LeftPtr;
if(isRoot(treePtr))
{
treePtr->clear();
delete treePtr;
treePtr=tempPtr;
return true;
}
return false;
}
else
{
treePtr->reSet(treePtr->lItem,treePtr->LeftPtr,treePtr->RightPtr);
return true;
}
}
}
else if(ch=='L')//L for ptr1 relative to ptr2's position is left
{
if(twoItem(treePtr->MiddlePtr))
{
treePtr->LeftPtr->reSet(treePtr->fItem,tempPtr,treePtr->MiddlePtr->LeftPtr);
treePtr->reSet(treePtr->MiddlePtr->fItem,treePtr->lItem,treePtr->LeftPtr,treePtr->MiddlePtr,treePtr->RightPtr);
treePtr->MiddlePtr->reSet(treePtr->MiddlePtr->lItem,treePtr->MiddlePtr->MiddlePtr,treePtr->MiddlePtr->RightPtr);
return true;
}
else
{
treePtr->MiddlePtr->reSet(treePtr->fItem,treePtr->MiddlePtr->lItem,tempPtr,treePtr->MiddlePtr->LeftPtr,treePtr->MiddlePtr->MiddlePtr);
treePtr->LeftPtr->clear();
delete treePtr->LeftPtr;
treePtr->LeftPtr=NULL;
if(oneItem(treePtr))
{
treePtr->setNumb(0);
tempPtr=treePtr->MiddlePtr;
if(isRoot(treePtr))
{
treePtr->clear();
delete treePtr;
treePtr=tempPtr;
return true;
}
return false;
}
else
{
treePtr->reSet(treePtr->lItem,treePtr->MiddlePtr,treePtr->RightPtr);
return true;
}
}
}
}
bool ttTree::insertMaintain(Node *&treePtr,Node *&tempPtr,
ItemType &tempItem,char ch)
throw(TTTException)
{
if(ch=='L')
{
if(twoItem(treePtr))
{
ItemType tranItem=treePtr->fItem;
Node *tranPtr=new Node(tempItem,tempPtr,treePtr->LeftPtr);
if(tranPtr==NULL)
throw TTTException("TTTException :allocate memory failed !");
treePtr->reSet(treePtr->lItem,treePtr->MiddlePtr,treePtr->RightPtr);
tempItem=tranItem;
tempPtr=tranPtr;
if(isRoot(treePtr))
{
Node *tempRoot=new Node(tempItem,tempPtr,treePtr);
if(tempRoot==NULL)
throw TTTException("TTTException :allocate memory failed !");
root=tempRoot;
return true;
}
return false;
}
else
{
treePtr->reSet(tempItem,treePtr->fItem,tempPtr,treePtr->LeftPtr,treePtr->MiddlePtr);
return true;
}
}
else if(ch=='M')
{
if(twoItem(treePtr))
{
Node *tranPtr=new Node(treePtr->fItem,treePtr->LeftPtr,tempPtr);
if(tranPtr==NULL)
throw TTTException("TTTEexception :allocate memory failed !");
treePtr->reSet(treePtr->lItem,treePtr->MiddlePtr,treePtr->RightPtr);
tempPtr=tranPtr;
if(isRoot(treePtr))
{
Node *tempRoot=new Node(tempItem,tempPtr,treePtr);
if(tempRoot==NULL)
throw TTTException("TTTException :allocate memory failed !");
root=tempRoot;
return true;
}
return false;
}
else
{
treePtr->reSet(treePtr->fItem,tempItem,treePtr->LeftPtr,
tempPtr,treePtr->MiddlePtr);
return true;
}
}
else if(ch=='R')
{
ItemType tranItem=treePtr->lItem;
Node *tranPtr=new Node(treePtr->fItem,treePtr->LeftPtr,treePtr->MiddlePtr);
if(tranPtr==NULL)
throw TTTException("TTTException :allocate memory failed !");
treePtr->reSet(tempItem,tempPtr,treePtr->RightPtr);
tempItem=tranItem;
tempPtr=tranPtr;
if(isRoot(treePtr))
{
Node *tempRoot=new Node(tempItem,tempPtr,treePtr);
if(tempRoot==NULL)
throw TTTException("TTTException :allocate memory failed !");
root=tempRoot;
return true;
}
return false;
}
else
{
if(twoItem(treePtr))
{
sort(treePtr->fItem,tempItem,treePtr->lItem);
Node *tranPtr=new Node(treePtr->fItem,NULL,NULL);
if(tranPtr==NULL)
throw TTTException("TTTException :allocate memory failed !");
treePtr->reSet(treePtr->lItem,NULL,NULL);
tempPtr=tranPtr;
if(isRoot(treePtr))
{
Node *tempRoot=new Node(tempItem,tempPtr,treePtr);
if(tempRoot==NULL)
throw TTTException("TTTException :allocate memory failed !");
root=tempRoot;
return true;
}
return false;
}
else
{
ItemType tranItem;
if(treePtr->fItem.getKey()>tempItem.getKey())
{
tranItem=treePtr->fItem;
treePtr->fItem=tempItem;
tempItem=tranItem;
}
treePtr->reSet(treePtr->fItem,tempItem,NULL,NULL,NULL);
return true;
}
}
}
2-3树的简单的测试程序
#include<iostream>
#include<cstdlib>
#include<string>
#include<ctime>
#include"two_three_tree.h"
using namespace std;
void display(ItemType &_item);
int main()
{
srand(time(0));
ttTree atree,btree;
ItemType aitem;
string thekey,thetel;
try{
for(int i=0;i<20;i++)
{
for(int j=0;j<7;j++)
{
thekey=thekey+char(rand()%26+'a');
thetel=thetel+char(rand()%10+'0');
}
atree.insert(ItemType(thekey,thetel));
thekey.clear();
thetel.clear();
}
cout<<"the item in atree :"<<endl;
atree.traverse(display);
cout<<"traverse over !"<<endl;
cout<<"calling operator= function :"<<endl;
btree=atree;
cout<<"the item in btree :"<<endl;
btree.traverse(display);
cout<<"traverse over !"<<endl;
}catch(TTTException &e)
{
cout<<e.what()<<endl;
}
return 0;
}
void display(ItemType &_item)
{
cout<<"the information :"<<_item.getKey()<<'\t'<<_item.getTel()<<endl;
}
有兴趣的可以自己测试一下。