输入前序+中序or中序+后序结合构造二叉树,并实现错误检测机制,最终实现可视化动态逐步建树,前中后序遍历二叉树。
首先放一下我的成果:
拿到题目的一瞬间我有点小懵,但完整做下来之后发现just so so。废话不多说,下面开始。
设计理念是:
1,创建一个树的节点结构体,我们采用三叉链表实现。
2,创建一个二叉树类,以节点为数据成员,用它的成员函数实现各个功能(建树,遍历,输出等)
3,直接输入前中后序序列,我采用字符数组加流的实现模式(比较有意思)
4,最后就是各个功能的具体实现。
目录
二叉树类
//节点结构体
struct bintreenode
{
char data;//数据域
int depth;//树的节点高度
int depth1;//树的层数
bintreenode* leftchild, * rightchild, * parent;
bintreenode(char x, int a = 0, int b = 0, bintreenode* p = NULL, bintreenode* q = NULL,
bintreenode* r = NULL) :data(x),depth(a),depth1(b),leftchild(p), rightchild(q), parent(r) {};
};
//二叉树类的声明
class binarytree
{
public:
bintreenode* root;
binarytree();
bintreenode* creattreep(char* arrp, char* arrm, int size, bintreenode* p, double x0, double y0, double x, double y,int n);//前序中序建立二叉树
bintreenode* creattreel(char* arrm, char* arrl, int size, bintreenode* p, double x0, double y0, double x, double y);//中序后序建立二叉树
bintreenode* coparent(bintreenode* p, bintreenode* q);
bintreenode* coparent1(char x, char y);//通过父指针获取公共父节点
int getdepth(bintreenode* p );
bintreenode* getdepth1(bintreenode* p);//获取深度
bintreenode* getx(bintreenode* p,char x);
bintreenode* gety(bintreenode* p, char x);//获取节点
void preoder(bintreenode* p);//前序遍历
void inoder(bintreenode* p);//中序遍历
void postoder(bintreenode* p);//后序遍历
};
数据输入
首先呢我们需要输入前中后序的序列,所以介绍一下我采用的流方法。
定义三个全局的字符数组,通过三个函数实现对他们的输入,然后在主函数中调用。
函数的实现是关键,先贴代码再解释。
void cinarryp()
{
char a;
string s;
cin >> s;
stringstream ss;
ss << s;
while (ss >> a)
{
arrp[n] = a;
n++;//计数
}
ss.clear();
}
首先用string类型的对象来存储我们在控制台中从键盘输入的数据,调用stringstream库(一个非常好用的格式转换库),它作为一个流,相当于一个中转站,在我们将string对象的内容放进流中后,再从流中读取到我们想要的地方,它会自动进行格式转换(太太太棒了!!)并且它是一个一个对象进行读取(这是我觉得非常好用的地方)
前序+中序建树
接下来是文章的核心部分了,最最关键的建树和可视化,依旧先上代码再解释
bintreenode* binarytree::creattreep(char* arrp, char* arrm,int size, bintreenode* p, double x0, double y0, double x, double y,int n)
{
if (size == 0)
{
return NULL;
}
bintreenode* roottemp = new bintreenode(arrp[0],0,NULL,NULL,p);
circle(x, y, 20);
setcolor(EGERGB(0xFF, 0xFF, 0x0));
outtextxy(x, y, roottemp->data);
Sleep(0.1 * 1000);
if(flag){}
else line(x0, y0, x, y );
Sleep(0.1 * 1000);
p = roottemp;
if (flag)
{
root = roottemp;
flag = 0;
}
int r = 0;
for (int i = 0; i < size; i++)
{
if (arrm[i] == arrp[0])
{
r = i;
break;
}
}
int t = pow(2, n );
int x1 = x - 400 / t;
int x2 = x + 400 / t;
roottemp->leftchild = creattreep(arrp + 1, arrm, r, p, x, y, x1, 100.0 * n,n+1);
roottemp->rightchild = creattreep(arrp + 1 + r, arrm + r + 1, size - 1 - r, p, x, y, x2, 100.0 * n,n+1);
return roottemp;
}
你会发现我的函数里面有好多参数,不要着急,下面会介绍我的心路历程。
首先是树最基本的递归建树了,只需要一个递归到头的终止判断和左右子树递归调用函数即可。
其次因为不是简单的用单一序列建树,所以需要构思实现方法,下面介绍。
举个栗子
结合我们学习的前中后序遍历,逆向思维去思考它,前序序列的第一个元素是根节点,在中序序列中找到根节点后,它的左边就是它的左子树,长度是中序序列根节点下标r,右边就是右子树,长度是n-r-1;接下来我们只需要往左右递归子树传递参数直到长度为0就ok了。大功告成!
遍历输出
树建好了,我们就可以输出了啊,输出它的前中后序遍历结果去验证一下。
void binarytree::preoder(bintreenode* p)
{
if (p != NULL)
{
cout << p->data;
preoder(p->leftchild);
preoder(p->rightchild);
}
}
太过简单就不做解释了。
可视化动态建树
接下来要实现可视化了!!老师让我们学会用EGE这个第三方库,第一次拿着它和它大眼瞪小眼,虽然知道它怎么用但是根本不知道怎么结合到我的代码里进行一步一步的实现建树啊。但是!睡觉的时候灵光一现:
1,只要在递归代码里,每创建一个节点的同时画一个节点就ok了。调用EGE的circle()函数。
2,难点也是重中之重在位置坐标,这里用到树的深度建立,贴个图来解释一下
n是树的深度,要想对于节点更多的树,他们仍旧不交叉并且美观,对于不同深度的节点就应该有相比于其父节点不同的偏移量:
第二层偏了200,是400/2;是2的1次方
第三层偏了100,是400/4;是2的2次方
第四层偏了50,是400/8;是2的3次方
所以代码中我们用一个变量t来存偏移量,节点左右子女分别是它本身加减偏移量即可。
3,这样节点都建立完了,可是树不能光有叶子,还得有茎啊,所以还要调用EGE的line()函数,可是画线要从一个点到另一个点,所以还要增加参数,将它父节点的坐标继承下下来。
4,这样树终于完整了,叶子和茎都有了,可是我要做动态建立啊!要一个一个蹦出来,EGE的delay()用不了,我借用了sleep()函数,真是人如其名,名如其功能,让你的电脑睡眠一小会就ok了!
5,大大大大功告成!像开头那样子的!
通过父指针寻找两个节点的公共父亲
bintreenode* binarytree::coparent(bintreenode* p, bintreenode* q)
{
if (p->parent == q->parent)
{
cout << "父节点是:" << endl;
cout << p->parent->data;
}
else if (p->depth1 > q->depth1) coparent(p->parent, q);
else coparent(p, q->parent);
return NULL;
}
通过节点之间的层数进行考虑。
检错机制
最后,做个简单的检错机制:
依旧是递归实现的,想法是:
1,当我们输入序列的元素个数都不相等时,肯定要输出错误了。
2,当前序中的元素在中序中找不到时,也要输出错误。
char* whethererror(char* arrp, char* arrm, int size)
{
if (size == 0) return NULL;
if (nflag)
{
if (!(strlen(arrp) == strlen(arrm)))
{
cout << "error!";
exit(0);
}
nflag = 0;
}
int flag = 1;
int r = 0;
for (int i = 0; i < size; i++)
{
if (arrp[0] == arrm[i])
{
r = i;
flag = 0;
break;
}
}
if (flag)
{
cout << "error!" << endl;
exit(0);
}
whethererror(arrp + 1, arrm, r);
whethererror(arrp + 1 + r, arrm + r + 1, size - 1 - r);
return NULL;
}
中序+后序当然是同样道理,略作改动即可。
最后的最后,感觉我太菜了,谢谢大噶伙!