北邮22信通一枚~
跟随课程进度每周更新数据结构与算法的代码和文章
持续关注作者 解锁更多邮苑信通专属代码~
上一篇文章:
下一篇文章:
目录
一.储存最简单数据类型的二叉树
代码部分:
代码效果:
运行结果:
二.存储相对较复杂的数据类型的二叉树
代码部分:
代码效果图:
运行结果:
***说明***
1.书上描绘的是采用二叉链表作为存储结构的二叉树。本代码完全遵从书上代码。小编最近会尝试用三叉链表写写(
2.遍历二叉链表的关键思想:递归。函数的递归调用。
3.书上只是给出了各种遍历方法在模板类中的书写方法,并有一定错误。本代码将其余部分完善(包括主函数和存储数据的数据类型)并将错误修改完毕,读者可以放心使用。本篇文章基于书上遍历算法,提供了两个例子。这两个例子的主要区别是,二叉树存储数据的数据类型不同。第一个例子是存储了简单的数据类型,比如int;第二个例子存储了相对较复杂的数据类型,为一个类。希望对读者有所帮助。
3.关于编写代码时新学到的知识:#define MAXSIZE 1e5这种写法是不被VS2022接受的。必须写成#define MAXSIZE 100000或者const int MAXSIZE=1e5;
4.有读者可能会这样问:在模板类中将root定义在public区域,这样能够保证数据的安全性吗?这样做的好处是什么?
首先回答第一个问题,数据相对安全。将根节点写在public中,外界只能访问到这一个结点中的数据,这对于想要得到数据的一方显然是毫无用处的;如果不通过成员函数的调用,外界就不能获取其他结点的信息。所以将root定义在public区域,能够保证数据的安全性。
第二个问题。这样做的好处,其实就是为了书写其他函数和主函数的时候调用根结点时能够方便一些。如果将root设置成为private中的数据也未尝不可,那就需要在public区域中书写一个getroot函数,具体实现函数如下:
binnode<temp>* getroot() { return this->root; }
后面每次用到root根节点的时候,都要调用一次这个函数(好麻烦)
5.想到啥再补充 有问题评论区找我
***说明结束***
一.储存最简单数据类型的二叉树
就是在主函数中,我们将temp的位置填入最简单的数据类型,实现最简单的二叉树遍历。这个二叉树每个结点中的数据域将都是这个最简单的数据类型,二叉树的结构看起来也相对简单。
本代码传入的数据类型是int。也就是我们建立了一个存储整型数据的二叉树。
下面我们来看代码:
代码部分:
#include<iostream>
#define MAXSIZE 100000
//注意:不能将上面这行宏定义写成
//#define MAXSIZE 1e5
//上面这样写会报错!!
using namespace std;
template<class temp>
struct binnode
{
temp data;
binnode<temp>* leftchild;
binnode<temp>* rightchild;
};
template<class temp>
class bintree
{
private:
void create(binnode<temp>*& r, temp data[], int i, int n);
void release(binnode<temp>* r);
public:
binnode<temp>* root;
bintree(temp data[], int n);
void preorder(binnode<temp>* r);
void inorder(binnode<temp>* r);
void postorder(binnode<temp>* r);
void levelorder(binnode<temp>* r);
~bintree();
};
template<class temp>
void bintree<temp>::create(binnode<temp>*& r, temp data[], int i, int n)
{
if (i <= n && data[i - 1] != 0)
{
r = new binnode<temp>;
r->data = data[i - 1];
r->leftchild = r->rightchild = NULL;
create(r->leftchild, data, 2 * i, n);
/*书上代码错误1:向函数传入实参时少传入一个n*/
create(r->rightchild, data, 2 * i + 1, n);/*书上代码错误同上*/
}
}
template<class temp>
bintree<temp>::bintree(temp data[], int n)
/*书上代码错误2:构造函数不能有返回值类型,但是书上多加了一个void*/
/*如果构造函数的声明语句写成
void bintree<temp>::bintree(temp data[], int n),
程序会报错:不能在构造函数上指定返回值类型*/
{
create(this->root, data, 1, n);
}
template<class temp>
void bintree<temp>::preorder(binnode<temp>* r)
{
if (r != NULL)
{
cout << r->data;//访问结点;
preorder(r->leftchild);//遍历左子树
preorder(r->rightchild);//遍历右子树
}
}
template<class temp>
void bintree<temp>::inorder(binnode<temp>* r)
{
if (r != NULL)
{
inorder(r->leftchild);
cout << r->data;
inorder(r->rightchild);
}
}
template<class temp>
void bintree<temp>::postorder(binnode<temp>* r)
{
if (r != NULL)
{
postorder(r->leftchild);
postorder(r->rightchild);
cout << r->data;
}
}
template<class temp>
void bintree<temp>::levelorder(binnode<temp>* R)
{
binnode<temp>* queue[MAXSIZE];
int f = 0, r = 0;
if (R != NULL)
queue[++r] = R;//根节点入队
while (f != r)
{
binnode<temp>* p = queue[++f];//队头元素入队
cout << p->data;//出队打印
if (p->leftchild != NULL)
queue[++r] = p->leftchild;//左孩子入队
if (p->rightchild != NULL)
queue[++r] = p->rightchild;//右孩子入队
}
}
template <class temp>
void bintree<temp>::release(binnode<temp>* r)
{
if (r != NULL)
{
release(r->leftchild);
release(r->rightchild);
delete r;
}
}
/*书上代码错误3:不能在析构函数上指定返回值类型,但是书上代码多了一个void*/
template<class temp>
bintree<temp>::~bintree()
{
release(this->root);
}
int main()
{
int a[5] = { 1,2,3,4,5 };
bintree<int>bintreee(a, 5);
cout << "前序遍历:" << endl;
bintreee.preorder(bintreee.root);
cout << endl << "中序遍历:" << endl;
bintreee.inorder(bintreee.root);
cout << endl << "后序遍历:" << endl;
bintreee.postorder(bintreee.root);
cout << endl << "层序遍历:" << endl;
bintreee.levelorder(bintreee.root);
return 0;
}
代码效果:
运行结果:
二.存储相对较复杂的数据类型的二叉树
定义了一个相对复杂的数据类型:一个类。需要注意的是,如果使用类作为存储的数据类型,有一个地方需要稍微改动一下:见原代码第59~63行。
代码部分:
#include<iostream>
#define MAXSIZE 100000
//注意:不能将上面这行宏定义写成
//#define MAXSIZE 1e5
//上面这样写会报错!!
using namespace std;
class student
{
private:
int ID;
string name;
public:
int existence;
student()
{
this->ID = 0;
this->name = "unknown name";
this->existence = 0;
}
student(int ID, string name)
{
this->ID = ID;
this->name = name;
this->existence = 1;
}
friend ostream& operator<<(ostream& output, student& s)
{
output << s.ID << " " << s.name << endl;
return output;
}
};
template<class temp>
struct binnode
{
temp data;
binnode<temp>* leftchild;
binnode<temp>* rightchild;
};
template<class temp>
class bintree
{
private:
void create(binnode<temp>*& r, temp data[], int i, int n);
void release(binnode<temp>* r);
public:
binnode<temp>* root;
bintree(temp data[], int n);
void preorder(binnode<temp>* r);
void inorder(binnode<temp>* r);
void postorder(binnode<temp>* r);
void levelorder(binnode<temp>* r);
~bintree();
};
template<class temp>
void bintree<temp>::create(binnode<temp>*& r, temp data[], int i, int n)
{
/*书上代码的一个问题:data[i-1]!=0会报错,这是因为data的数据类型是temp,没法实现!=的运算*/
//书上原代码
//if (i <= n && data[i - 1] != 0)
if (i <= n && data[i - 1].existence != 0)
{
r = new binnode<temp>;
r->data = data[i - 1];
r->leftchild = r->rightchild = NULL;
create(r->leftchild, data, 2 * i, n);/*书上代码错误1:向函数传入实参时少传入一个n*/
create(r->rightchild, data, 2 * i + 1, n);/*书上代码错误同上*/
}
}
template<class temp>
bintree<temp>::bintree(temp data[], int n)
/*书上代码错误2:构造函数不能有返回值类型,但是书上多加了一个void*/
/*如果构造函数的声明语句写成
void bintree<temp>::bintree(temp data[], int n),
程序会报错:不能在构造函数上指定返回值类型*/
{
create(this->root, data, 1, n);
}
template<class temp>
void bintree<temp>::preorder(binnode<temp>* r)
{
if (r != NULL)
{
cout << r->data;//访问结点;
preorder(r->leftchild);//遍历左子树
preorder(r->rightchild);//遍历右子树
}
}
template<class temp>
void bintree<temp>::inorder(binnode<temp>* r)
{
if (r != NULL)
{
inorder(r->leftchild);
cout << r->data;
inorder(r->rightchild);
}
}
template<class temp>
void bintree<temp>::postorder(binnode<temp>* r)
{
if (r != NULL)
{
postorder(r->leftchild);
postorder(r->rightchild);
cout << r->data;
}
}
template<class temp>
void bintree<temp>::levelorder(binnode<temp>* R)
{
binnode<temp>* queue[MAXSIZE];
int f = 0, r = 0;
if (R != NULL)
queue[++r] = R;//根节点入队
while (f != r)
{
binnode<temp>* p = queue[++f];//队头元素入队
cout << p->data;//出队打印
if (p->leftchild != NULL)
queue[++r] = p->leftchild;//左孩子入队
if (p->rightchild != NULL)
queue[++r] = p->rightchild;//右孩子入队
}
}
template <class temp>
void bintree<temp>::release(binnode<temp>* r)
{
if (r != NULL)
{
release(r->leftchild);
release(r->rightchild);
delete r;
}
}
/*书上代码错误3:不能在析构函数上指定返回值类型,但是书上代码多了一个void*/
template<class temp>
bintree<temp>::~bintree()
{
release(this->root);
}
int main()
{
system("color 0A");
student stu[5] = { {1,"zhang"},{2,"wang"},{3,"li"},{4,"zhao"},{5,"liu"} };
bintree<student>bintreee(stu, 5);
cout << "前序遍历:" << endl;
bintreee.preorder(bintreee.root);
/*说明:这里体现了将根节点定义为public类型的好处,
不然需要通过一个成员函数来实现这个功能,
从数据保密性来看,这样做也是可以的:
外部如果不通过调用成员函数,就只能访问根节点一个节点内的数据,
但是其他任意节点内的数据都无法访问,安全性也相对较高。
*/
cout << endl << "中序遍历:" << endl;
bintreee.inorder(bintreee.root);
cout << endl << "后序遍历:" << endl;
bintreee.postorder(bintreee.root);
cout << endl << "层序遍历:" << endl;
bintreee.levelorder(bintreee.root);
cout << endl;
return 0;
}