一、实验题目及要求
题目:基于前序、中序、后序序列构造二叉树
需求:
1、任意输入前序+中序序列或者中序+后序序列,生成二叉树,请使用三叉链表,在构造链表的过程中同步更新每个节点的parent指针;
2、检测输入的前序,中序,后续序列的有效性,例如当用户输入错误的序列时,程序应该有错误提示;
3、利用打印二叉树功能显示二叉树的逐步构造过程(不是仅仅把最后构造的树显示,而是要把算法运行过程中树的每一步的构造过程动态演示出来,即显示中间过程)。
二、概要设计
根据前序+中序序列创建二叉树的基本思路:前序的遍历顺序为根左右,中序的遍历顺序为左根右,根据前序和中序遍历的差异我们可以得到如下的规则:
一、前序遍历的第一个元素即为二叉树的根结点;
二、在中序遍历序列中找到前序序列的根结点,则该结点左侧为左子树的中序遍历序列,右侧为右子树的中序遍历序列;
三、左子树的结点个数为n,在前序序列去除根结点后前n个为左子树的前序序列,第n个之后的是右子树前序序列;
四、得到左子树和右子树的前序、中序序列进行递归,递归结束的条件是左右子树均为空。
根据中序+后序序列创建二叉树的基本思路:中序的遍历顺序为左根右,后序的遍历顺序为左右根,根据中序和后序遍历的差异我们可以得到如下的规则:
一、后序遍历的最后一个元素即为二叉树的根结点;
二、在中序遍历序列中找到前序序列的根结点,则该结点左侧为左子树的中序遍历序列,右侧为右子树的中序遍历序列;
三、左子树的结点个数为n,在后序序列去除根结点后前n个为左子树的前序序列,第n个之后的是右子树前序序列;
四、得到左子树和右子树的前序、中序序列进行递归,递归结束的条件是左右子树均为空。
可以看出,前序+中序序列和中序+后序序列的区别仅在于获取根节点的方式从前序第一个变成了后序最后一个。
三、详细设计
二叉树结点类:
template <class T>
struct BinTreeNode
{
T data;
BinTreeNode<T>* leftChild, * rightChild, * parent;
int x0, y0, m;
BinTreeNode(BinTreeNode<T>* p)
{
data = '*'; leftChild = NULL; rightChild = NULL; parent = p;
}
BinTreeNode()
{
data = '*'; leftChild = NULL; rightChild = NULL; parent = NULL;
}
};
二叉树类:
template <class T>
class BinaryTree
{
public:
BinaryTree() { root = new BinTreeNode<T>; root->parent = NULL; }
BinTreeNode<T>* Parent(BinTreeNode <T>* t)
{ return t.parent; }
BinTreeNode<T>* LeftChild(BinTreeNode<T>* t) {
return (t != NULL) ? t->leftChild : NULL;
}
BinTreeNode<T>* RightChild(BinTreeNode<T>* t) {
return (t != NULL) ? t->rightChild : NULL;
}
BinTreeNode<T>* getRoot() const { return root; }
BinTreeNode<T>* CoParent(BinTreeNode<T>* p, BinTreeNode<T>* q);
void Menu();
bool Num(string s, string t);
bool Examine_1(string str1, string str2);
bool Examine_2(string str1, string str2);
void Find(BinTreeNode<T>* d);
protected:
BinTreeNode<T>* root; //二叉树的根
T RefValue; //数据输入停止标志
void CreateBinTree1(BinTreeNode<T>* t, string str1, string str2);//前+中序遍历创建建树
void CreateBinTree2(BinTreeNode<T>* t, string str1, string str2);//中+后序遍历创建建树
private:
string str1;
string str2;
};
主函数运行时,首先提示用户输入前序+中序序列还是中序+后序序列,待用户输入后,检查输入序列是否能够建树。对于前序+中序序列,遍历整个字符串,第一个即为根结点,保存在char型的rootNode中,接着寻找根在中序序列中的位置,如果没有找到根节点,则终止检查函数,返回0,说明该序列不能构建二叉树;如果找到了根节点,则分别截取中序里根节点左侧元素和右侧元素,分别为左子树和右子树,继续递归地检查。
template<class T>
inline bool BinaryTree<T>::Examine_1(string str1, string str2)
{
if (str1[0] != NULL && str2[0] != NULL)//判断序列是否遍历完毕
{
int i = 0, j = 0;
char rootNode = str1[0];//根
int p = str2.find(rootNode);//根在中序序列中的位置
if (p == -1) return false;//如果未找到根节点,则终止检查函数,返回0,不能构建为二叉树
if (p >= 0)
{
i = Examine_1(str1.substr(1, p), str2.substr(0, p));//找到了根节点,则分别截取中序里根节点左侧元素
if (i == 0) {
cout << "这两个序列不能建树,原因是:前序序列的左子树:" << str1.substr(0, p) << "和中序序列的左子树" << str2.substr(0, p) << "不匹配" << endl;
} //从第一个元素开始截取,截取p个元素
}
//截取中序序列的右子树和前序序列的剩下部分
if (p < str1.size())
{
j = Examine_1(str1.substr(p + 1, (str1.size() - 1 - p)), str2.substr(p + 1, (str2.size() - 1 - p)));
if (j == 0) {
cout << "这两个序列不能建树,原因是:前序序列的右子树:" << str1.substr(p + 1, (str1.size() - 1 - p)) << "和中序序列的右子树" << str2.substr(p + 1, (str2.size() - 1 - p)) << "不匹配" << endl;
}
}
//当二者都检测不出错误时,最后返回1,表示可以打印出二叉树
return (i + j == 2) ? true : false;//必须i=j=1,才表示能够构建为二叉树
}
else return true;
}
对于中序+后序序列,遍历整个字符串,最后一个即为根结点,保存在char型的rootNode中,接着寻找根在中序序列中的位置,如果没有找到根节点,则终止检查函数,返回0,说明该序列不能构建二叉树;如果找到了根节点,则分别截取中序里根节点左侧元素和右侧元素,分别为左子树和右子树,继续递归地检查直至结束。
template<class T>
inline bool BinaryTree<T>::Examine_2(string str1, string str2)
{
if (str1[0] != NULL && str2[0] != NULL)
{
int i = 0, j = 0;
char rootNode = str2[str2.length() - 1];//根
int p = str1.find(rootNode);//根在中序序列中的位置
if (p == -1) return false;
if (p >= 0)
{
i = Examine_2(str1.substr(0, p), str2.substr(0, p));
if (i == 0) {
cout << "这两个序列不能建树,原因是:前序序列的右子树:" << str1.substr(0, p) << "和中序序列的右子树" << str2.substr(0, p) << "不匹配" << endl;
}
}
if (p < str1.size())
{
j = Examine_2(str1.substr(p + 1, (str1.size() - 1 - p)), str2.substr(p, (str2.size() - 1 - p)));
if (j == 0) {
cout << "这两个序列不能建树,原因是:前序序列的左子树:" << str1.substr(p + 1, (str1.size() - 1 - p)) << "和中序序列的左子树" << str2.substr(p + 1, (str2.size() - 1 - p)) << "不匹配" << endl;
}
}
return (i + j == 2) ? true : false;
}
else return true;
}
如果检查没有问腿,则开始建树:
template<class T>
void BinaryTree<T>::CreateBinTree1(BinTreeNode<T>* t, string str1, string str2)
{
int dy = 100;//上下距离
int x = width / 2;
t->x0 = x;
t->y0 = 40;
//前+中序遍历创建二叉树
BinTreeNode<T>* NextL = new BinTreeNode<T>(t); //左孩子
BinTreeNode<T>* NextR = new BinTreeNode<T>(t); //右孩子
if (str1.length() == 0)
{
t = NULL;
delete NextL;
delete NextR;
return;
}
t->leftChild = NextL;
t->rightChild = NextR;
char rootNode = str1[0];//根
int index = str2.find(rootNode);//根在中序序列中的位置
string lchild_str2 = str2.substr(0, index);//左孩子的中序序列
string rchild_str2 = str2.substr(index + 1);//右孩子的中序序列
int lchild_length = lchild_str2.length();//左孩子的长度
int rchild_length = rchild_str2.length();//右孩子的长度
string lchild_str1 = str1.substr(1, lchild_length);//左孩子的前序序列
string rchild_str1 = str1.substr(1 + lchild_length);
if (t != NULL)
{
t->data = rootNode;
if (t != NULL && t->data != '*')
{
if (t->parent == NULL) {
t->x0 = x;
t->y0 = 40;
t->m = width / 4;
circle(t->x0, t->y0, r);
xyprintf(t->x0 - 2, t->y0 - 7, "%c", t->data);
Sleep(1000);
}
else {
if (t == t->parent->leftChild) {
t->m = t->parent->m * 3 / 5;
t->y0 = t->parent->y0 + dy;
t->x0 = t->parent->x0 - t->m;
ege_line(t->parent->x0, t->parent->y0, t->x0, t->y0);
circle(t->x0, t->y0, r);
xyprintf(t->x0, t->y0, "%c", t->data);
Sleep(1000);
}
if (t == t->parent->rightChild) {
t->m = t->parent->m * 3 / 5;
t->y0 = t->parent->y0 + dy;
t->x0 = t->parent->x0 + t->m;
ege_line(t->parent->x0, t->parent->y0, t->x0, t->y0);
circle(t->x0, t->y0, r);
xyprintf(t->x0, t->y0, "%c", t->data);
Sleep(1000);
}
}
}
CreateBinTree1(NextR, rchild_str1, rchild_str2);//递归创建右孩子
CreateBinTree1(NextL, lchild_str1, lchild_str2);//递归创建左孩子
}
}
template<class T>
inline void BinaryTree<T>::CreateBinTree2(BinTreeNode<T>* t, string str1, string str2)
{
int dy = 120;
int x = width / 2;
t->x0 = x;
t->y0 = 40;
BinTreeNode<T>* NextL = new BinTreeNode<T>(t);//左孩子
BinTreeNode<T>* NextR = new BinTreeNode<T>(t);//右孩子
if (str1.length() == 0)
{
t = NULL;
delete NextL;
delete NextR;
return;
}
t->leftChild = NextL;
t->rightChild = NextR;
char rootNode = str2[str2.length() - 1];//根
int index = str1.find(rootNode);//根在中序序列中的位置
string lchild_str1 = str1.substr(0, index);//左孩子的中序序列
string rchild_str1 = str1.substr(index + 1);//右孩子的中序序列
int lchild_length = lchild_str1.length();//左孩子的长度
int rchild_length = rchild_str1.length();//右孩子的长度
string lchild_str2 = str2.substr(0, lchild_length);//左孩子的后序序列
string rchild_str2 = str2.substr(lchild_length, rchild_length);//右孩子的后序序列
if (t != NULL)
{
t->data = rootNode;
if (t != NULL && t->data != '*')
{
if (t->parent == NULL) {
t->x0 = x;
t->y0 = 40;
t->m = width / 4;
circle(t->x0, t->y0, r);
xyprintf(t->x0 - 2, t->y0 - 7, "%c", t->data);
Sleep(1000);
}
else {
if (t == t->parent->leftChild) {
t->m = t->parent->m * 3 / 5;
t->y0 = t->parent->y0 + dy;
t->x0 = t->parent->x0 - t->m;
ege_line(t->parent->x0, t->parent->y0, t->x0, t->y0);
circle(t->x0, t->y0, r);
xyprintf(t->x0, t->y0, "%c", t->data);
Sleep(1000);
}
if (t == t->parent->rightChild) {
t->m = t->parent->m * 3 / 5;
t->y0 = t->parent->y0 + dy;
t->x0 = t->parent->x0 + t->m;
ege_line(t->parent->x0, t->parent->y0, t->x0, t->y0);
circle(t->x0, t->y0, r);
xyprintf(t->x0, t->y0, "%c", t->data);
Sleep(1000);
}
}
}
CreateBinTree2(NextR, rchild_str1, rchild_str2);//递归创建右孩子
CreateBinTree2(NextL, lchild_str1, lchild_str2);//递归创建左孩子
}
}
四、测试结果
主界面:
前序+中序序列:
中序+后序序列:
错误提示:
五、源代码
BinTree.h
#include <iostream>
#include <string>
#include<queue>
#include<graphics.h>
#define r 18
#define width 1000
#define height 1000
using namespace std;
template <class T>
struct BinTreeNode
{
T data;
BinTreeNode<T>* leftChild, * rightChild, * parent;
int x0, y0, m;
//左子女、右子女链域
BinTreeNode(BinTreeNode<T>* p)
{
data = '*'; leftChild = NULL; rightChild = NULL; parent = p;
}
BinTreeNode()
{
data = '*'; leftChild = NULL; rightChild = NULL; parent = NULL;
}
};
template <class T>
class BinaryTree
{
public:
BinaryTree() { root = new BinTreeNode<T>; root->parent = NULL; }
BinTreeNode<T>* Parent(BinTreeNode <T>* t)
{ return t.parent; }
BinTreeNode<T>* LeftChild(BinTreeNode<T>* t) {
return (t != NULL) ? t->leftChild : NULL;
}
BinTreeNode<T>* RightChild(BinTreeNode<T>* t) {
return (t != NULL) ? t->rightChild : NULL;
}
BinTreeNode<T>* getRoot() const { return root; }
BinTreeNode<T>* CoParent(BinTreeNode<T>* p, BinTreeNode<T>* q);
void Menu(); //菜单
bool Num(string s, string t);
bool Examine_1(string str1, string str2);
bool Examine_2(string str1, string str2);
void Find(BinTreeNode<T>* d);
protected:
BinTreeNode<T>* root; //二叉树的根
T RefValue; //数据输入停止标志
void CreateBinTree1(BinTreeNode<T>* t, string str1, string str2);//前+中序遍历创建建树
void CreateBinTree2(BinTreeNode<T>* t, string str1, string str2);//中+后序遍历创建建树
private:
string str1;
string str2;
};
CreateBinaryTree.cpp
#define SHOW_CONSOLE
#include<iostream>
#include <string>
#include<queue>
#include<windows.h>
#include "BinTree.h"
using namespace std;
template<class T>
inline BinTreeNode<T>* BinaryTree<T>::CoParent(BinTreeNode<T>* p, BinTreeNode<T>* q)
{
string str;
char s;
int i = 0;
int n;
BinTreeNode<T>* ch = new BinTreeNode<T>;
ch = p;
if (p->parent != NULL)
{
str += p->parent->data;
p = p->parent;
}
if (q->parent != NULL)
{
s = q->parent->data;
n = str.find(s);
if (n == -1)
q = q->parent;
}
ch = ch->parent;
return ch;
}
template<class T>
inline void BinaryTree<T>::Menu()
{
cout << endl;
cout << "1.前、中序遍历创建二叉树;" << endl;
cout << "2.中、后序遍历建立二叉树。" << endl;
cout << "0.退出" << endl;
int ch;
bool p;
cin >> ch;
switch (ch)
{
case 1:
{
cout << "请输入前序:";
cin >> str1;
cout << "请输入中序:";
cin >> str2;
p = Num(str1, str2);
if (p)
{
p = Examine_1(str1, str2);
if (p)
{
initgraph(width, height, 0);
setbkcolor(WHITE);
setcolor(BLACK);
ege_enable_aa(true);
CreateBinTree1(root, str1, str2);
getch();
closegraph();
clearviewport();
Menu();
}
else
Menu();
}
else
cout << "这两个序列长度不等,无法建树" << endl;
Menu();
}break;
case 2:
{
cout << "请输入中序:";
cin >> str1;
cout << "请输入后序:";
cin >> str2;
p = Num(str1, str2);
if (p)
{
p = Examine_2(str1, str2);
if (p)
{
initgraph(width, height, 0);
setbkcolor(WHITE);
setcolor(BLACK);
ege_enable_aa(true);
CreateBinTree2(root, str1, str2);
getch();
closegraph();
clearviewport();
Menu();
}
else
Menu();
}
else
cout << "这两个序列长度不等,无法建树" << endl;
Menu();
}break;
case 0:
{
exit(1);
}
default: {
cout << "输入非法,请重新输入:" << endl;
Menu();
}break;
}
}
template<class T>
inline bool BinaryTree<T>::Num(string s, string t) //判断两序列的长度是否一样,一样则返回真
{
int x = s.length();
int y = t.length();
if (x == y) return true;
else
return false;
}
template<class T>
inline bool BinaryTree<T>::Examine_1(string str1, string str2)
{
if (str1[0] != NULL && str2[0] != NULL)//判断序列是否遍历完毕
{
int i = 0, j = 0;
char rootNode = str1[0];//根
int p = str2.find(rootNode);//根在中序序列中的位置
if (p == -1) return false;//如果未找到根节点,则终止检查函数,返回0,不能构建为二叉树
if (p >= 0)
{
i = Examine_1(str1.substr(1, p), str2.substr(0, p));//找到了根节点,则分别截取中序里根节点左侧元素
if (i == 0) {
cout << "这两个序列不能建树,原因是:前序序列的左子树:" << str1.substr(0, p) << "和中序序列的左子树" << str2.substr(0, p) << "不匹配" << endl;
} //从第一个元素开始截取,截取p个元素
}
//截取中序序列的右子树和前序序列的剩下部分
if (p < str1.size())
{
j = Examine_1(str1.substr(p + 1, (str1.size() - 1 - p)), str2.substr(p + 1, (str2.size() - 1 - p)));
if (j == 0) {
cout << "这两个序列不能建树,原因是:前序序列的右子树:" << str1.substr(p + 1, (str1.size() - 1 - p)) << "和中序序列的右子树" << str2.substr(p + 1, (str2.size() - 1 - p)) << "不匹配" << endl;
}
}
//当二者都检测不出错误时,最后返回1,表示可以打印出二叉树
return (i + j == 2) ? true : false;//必须i=j=1,才表示能够构建为二叉树
}
else return true;
}
template<class T>
inline bool BinaryTree<T>::Examine_2(string str1, string str2)
{
if (str1[0] != NULL && str2[0] != NULL)
{
int i = 0, j = 0;
char rootNode = str2[str2.length() - 1];//根
int p = str1.find(rootNode);//根在中序序列中的位置
if (p == -1) return false;
if (p >= 0)
{
i = Examine_2(str1.substr(0, p), str2.substr(0, p));
if (i == 0) {
cout << "这两个序列不能建树,原因是:前序序列的右子树:" << str1.substr(0, p) << "和中序序列的右子树" << str2.substr(0, p) << "不匹配" << endl;
}
}
if (p < str1.size())
{
j = Examine_2(str1.substr(p + 1, (str1.size() - 1 - p)), str2.substr(p, (str2.size() - 1 - p)));
if (j == 0) {
cout << "这两个序列不能建树,原因是:前序序列的左子树:" << str1.substr(p + 1, (str1.size() - 1 - p)) << "和中序序列的左子树" << str2.substr(p + 1, (str2.size() - 1 - p)) << "不匹配" << endl;
}
}
return (i + j == 2) ? true : false;
}
else return true;
}
template<class T>
void BinaryTree<T>::CreateBinTree1(BinTreeNode<T>* t, string str1, string str2)
{
int dy = 100;//上下距离
int x = width / 2;
t->x0 = x;
t->y0 = 40;
//前+中序遍历创建二叉树
BinTreeNode<T>* NextL = new BinTreeNode<T>(t); //左孩子
BinTreeNode<T>* NextR = new BinTreeNode<T>(t); //右孩子
if (str1.length() == 0)
{
t = NULL;
delete NextL;
delete NextR;
return;
}
t->leftChild = NextL;
t->rightChild = NextR;
char rootNode = str1[0];//根
int index = str2.find(rootNode);//根在中序序列中的位置
string lchild_str2 = str2.substr(0, index);//左孩子的中序序列
string rchild_str2 = str2.substr(index + 1);//右孩子的中序序列
int lchild_length = lchild_str2.length();//左孩子的长度
int rchild_length = rchild_str2.length();//右孩子的长度
string lchild_str1 = str1.substr(1, lchild_length);//左孩子的前序序列
string rchild_str1 = str1.substr(1 + lchild_length);
if (t != NULL)
{
t->data = rootNode;
if (t != NULL && t->data != '*')
{
if (t->parent == NULL) {
t->x0 = x;
t->y0 = 40;
t->m = width / 4;
circle(t->x0, t->y0, r);
xyprintf(t->x0 - 2, t->y0 - 7, "%c", t->data);
Sleep(1000);
}
else {
if (t == t->parent->leftChild) {
t->m = t->parent->m * 3 / 5;
t->y0 = t->parent->y0 + dy;
t->x0 = t->parent->x0 - t->m;
ege_line(t->parent->x0, t->parent->y0, t->x0, t->y0);
circle(t->x0, t->y0, r);
xyprintf(t->x0, t->y0, "%c", t->data);
Sleep(1000);
}
if (t == t->parent->rightChild) {
t->m = t->parent->m * 3 / 5;
t->y0 = t->parent->y0 + dy;
t->x0 = t->parent->x0 + t->m;
ege_line(t->parent->x0, t->parent->y0, t->x0, t->y0);
circle(t->x0, t->y0, r);
xyprintf(t->x0, t->y0, "%c", t->data);
Sleep(1000);
}
}
}
CreateBinTree1(NextR, rchild_str1, rchild_str2);//递归创建右孩子
CreateBinTree1(NextL, lchild_str1, lchild_str2);//递归创建左孩子
}
}
template<class T>
inline void BinaryTree<T>::CreateBinTree2(BinTreeNode<T>* t, string str1, string str2)
{
int dy = 120;
int x = width / 2;
t->x0 = x;
t->y0 = 40;
BinTreeNode<T>* NextL = new BinTreeNode<T>(t);//左孩子
BinTreeNode<T>* NextR = new BinTreeNode<T>(t);//右孩子
if (str1.length() == 0)
{
t = NULL;
delete NextL;
delete NextR;
return;
}
t->leftChild = NextL;
t->rightChild = NextR;
char rootNode = str2[str2.length() - 1];//根
int index = str1.find(rootNode);//根在中序序列中的位置
string lchild_str1 = str1.substr(0, index);//左孩子的中序序列
string rchild_str1 = str1.substr(index + 1);//右孩子的中序序列
int lchild_length = lchild_str1.length();//左孩子的长度
int rchild_length = rchild_str1.length();//右孩子的长度
string lchild_str2 = str2.substr(0, lchild_length);//左孩子的后序序列
string rchild_str2 = str2.substr(lchild_length, rchild_length);//右孩子的后序序列
if (t != NULL)
{
t->data = rootNode;
if (t != NULL && t->data != '*')
{
if (t->parent == NULL) {
t->x0 = x;
t->y0 = 40;
t->m = width / 4;
circle(t->x0, t->y0, r);
xyprintf(t->x0 - 2, t->y0 - 7, "%c", t->data);
Sleep(1000);
}
else {
if (t == t->parent->leftChild) {
t->m = t->parent->m * 3 / 5;
t->y0 = t->parent->y0 + dy;
t->x0 = t->parent->x0 - t->m;
ege_line(t->parent->x0, t->parent->y0, t->x0, t->y0);
circle(t->x0, t->y0, r);
xyprintf(t->x0, t->y0, "%c", t->data);
Sleep(1000);
}
if (t == t->parent->rightChild) {
t->m = t->parent->m * 3 / 5;
t->y0 = t->parent->y0 + dy;
t->x0 = t->parent->x0 + t->m;
ege_line(t->parent->x0, t->parent->y0, t->x0, t->y0);
circle(t->x0, t->y0, r);
xyprintf(t->x0, t->y0, "%c", t->data);
Sleep(1000);
}
}
}
CreateBinTree2(NextR, rchild_str1, rchild_str2);//递归创建右孩子
CreateBinTree2(NextL, lchild_str1, lchild_str2);//递归创建左孩子
}
}
int main()
{
BinaryTree<char>s;
s.Menu();
return 0;
}