为了一般性的表示树结构,我们通常要采用链式存储的方法来存储。这里我将分享一种通过先将数据存到左子树而后存到右子树以此类推的存储方法。主要思想是采用递归。
第一步:
定义Tree类:
template<class T>
class Tree{
private:
T data; //数据域
Tree<T> *Lchild; //左孩子
Tree<T> *Rchild; //右孩子
static int i,j; //计数
void error(ErrorType error) const;
public:
Tree(){Lchild= nullptr;Rchild= nullptr;data='0';}//构造函数
void RootTree(T &a);
void InsertLeft(Tree<T>*& leftchild,T &a);
void InsertRight(Tree<T>*& rightchild,T &a);
void TreeInsert(T &a);
~Tree();
void Visit();
};
template<class T>
int Tree<T>::i=0;
template<class T>
int Tree<T>::j=0;
这里我们需要定义两个计数器,方便后续更加直观的理解树结构。这里我主要是将左节点和右节点、根节点的插入分开,分别定义为成员函数,这样方便后续修改代码和查找代码的漏洞,也是为了代码逻辑更加清晰。
补充的是:
我还增加了一些错误判断:
enum ErrorType{memoryerror,indexvisiterror};
string errorMsg[]={"memoryerror","indexvisiterror"};
//error
template<class T>
void Tree<T>::error(ErrorType error) const {
cout<<errorMsg[error];
exit(1);
}
这个函数主要是进行错误判断,判断后续生成新节点是否成功分配内存空间。
接下来是对成员函数的实现:
左孩子生成函数:
//左节点
template<class T>
void Tree<T>::InsertLeft(Tree<T>*& leftchild,T &left) {
leftchild=new Tree<T>;
if (leftchild== nullptr) error(memoryerror);
this->Lchild=leftchild;
cout<<"左"<<++i<<"节点:";
cin>>left;
leftchild->data=left;
leftchild->Lchild= nullptr;
leftchild->Rchild= nullptr;
}
首先调用该函数的时候就要生成一个新的内存空间,而后将新生成的左右孩子的左右节点赋为空值,将根节点的左节点指向新生成的左孩子的数据域。
右孩子生成函数也类似左孩子生成函数:
//右孩子
template<class T>
void Tree<T>::InsertRight(Tree<T>*& rightchild,T &right) {
rightchild= new Tree<T>;
if (rightchild == nullptr) error(memoryerror);
this->Rchild = rightchild;
cout << "右"<<++j<<"节点:";
cin >> right;
rightchild->data=right;
rightchild->Lchild = nullptr;
rightchild->Rchild = nullptr;
}
接下来就是根节点生成函数:
//根插入
template<class T>
void Tree<T>::RootTree(T &a) {
cout << "根节点的值:";
cin >> a;
this->data = a;
this->Lchild= nullptr;
this->Rchild= nullptr;
}
三个函数都实现完毕后,我们要将它们串起来,联动起来共同实现树结构,这时候我们需要借助一个新的函数来控制它们。
//Tree Insert
template<class T>
void Tree<T>::TreeInsert(T &a) {
if (this->data == '0') {
RootTree(a); // 如果树为空,则插入根节点
} else if (this->Lchild == nullptr) {
InsertLeft(this->Lchild, a); // 如果左节点为空,则插入左节点
} else if (this->Rchild == nullptr) {
InsertRight(this->Rchild, a); // 如果右节点为空,则插入右节点
} else {
// 左右节点都已经存在,从左节点开始新建一颗子树
this->Lchild->TreeInsert(a);
}
}
接下来就是析构函数了,因为使用了new,所以在析构函数中要用delete来释放内存。并将左右节点指针置为空指针,防止产生迷途指针。
//析构函数
template<class T>
Tree<T>::~Tree() {
delete Lchild;
Lchild = nullptr;
delete Rchild;
Rchild = nullptr;
}
最后就是访问函数了,这里采用中序递归查找。
//访问
template<class T>
void Tree<T>::Visit() {
if (this == nullptr) {
cout << "此树为空树" << endl;
return;
}
// 中序遍历:左子树 -> 根节点 -> 右子树
if (Lchild != nullptr) {
Lchild->Visit();
}
cout << data;//输出数据域
if (Rchild != nullptr) {
Rchild->Visit();
}
}
最后的最后就是main函数生成一个Tree对象了:
int main(){
Tree<char> S;
char elem;int i=0;
while (i<=5) {
S.TreeInsert(elem);
i++;
}
S.Visit();
return 0;
}
接下来看看运行结果吧:
以下是完整代码:
#include<iostream>
using namespace std;
enum ErrorType{memoryerror,indexvisiterror};
string errorMsg[]={"memoryerror","indexvisiterror"};
template<class T>
class Tree{
private:
T data; //数据域
Tree<T> *Lchild; //左孩子
Tree<T> *Rchild; //右孩子
static int i,j; //计数
void error(ErrorType error) const;
public:
Tree(){Lchild= nullptr;Rchild= nullptr;data='0';}//构造函数
void RootTree(T &a);
void InsertLeft(Tree<T>*& leftchild,T &a);
void InsertRight(Tree<T>*& rightchild,T &a);
void TreeInsert(T &a);
~Tree();
void Visit();
};
template<class T>
int Tree<T>::i=0;
template<class T>
int Tree<T>::j=0;
//error
template<class T>
void Tree<T>::error(ErrorType error) const {
cout<<errorMsg[error];
exit(1);
}
//左孩子
template<class T>
void Tree<T>::InsertLeft(Tree<T>*& leftchild,T &left) {
leftchild=new Tree<T>;
if (leftchild== nullptr) error(memoryerror);
this->Lchild=leftchild;
cout<<"左"<<++i<<"节点:";
cin>>left;
leftchild->data=left;
leftchild->Lchild= nullptr;
leftchild->Rchild= nullptr;
}
//右孩子
template<class T>
void Tree<T>::InsertRight(Tree<T>*& rightchild,T &right) {
rightchild= new Tree<T>;
if (rightchild == nullptr) error(memoryerror);
this->Rchild = rightchild;
cout << "右"<<++j<<"节点:";
cin >> right;
rightchild->data=right;
rightchild->Lchild = nullptr;
rightchild->Rchild = nullptr;
}
//析构函数
template<class T>
Tree<T>::~Tree() {
delete Lchild;
Lchild = nullptr;
delete Rchild;
Rchild = nullptr;
}
//根插入
template<class T>
void Tree<T>::RootTree(T &a) {
cout << "根节点的值:";
cin >> a;
this->data = a;
this->Lchild= nullptr;
this->Rchild= nullptr;
}
//Tree Insert
template<class T>
void Tree<T>::TreeInsert(T &a) {
if (this->data == '0') {
RootTree(a); // 如果树为空,则插入根节点
} else if (this->Lchild == nullptr) {
InsertLeft(this->Lchild, a); // 如果左节点为空,则插入左节点
} else if (this->Rchild == nullptr) {
InsertRight(this->Rchild, a); // 如果右节点为空,则插入右节点
} else {
// 左右节点都已经存在,从左节点开始新建一颗子树
this->Lchild->TreeInsert(a);
}
}
//访问
template<class T>
void Tree<T>::Visit() {
if (this == nullptr) {
cout << "此树为空树" << endl;
return;
}
// 中序遍历:左子树 -> 根节点 -> 右子树
if (Lchild != nullptr) {
Lchild->Visit();
}
cout << data;//输出数据域
if (Rchild != nullptr) {
Rchild->Visit();
}
}
int main(){
Tree<char> S;
char elem;int i=0;
while (i<=5) {
S.TreeInsert(elem);
i++;
}
S.Visit();
return 0;
}