C++代码实现中缀表达式转化为二叉树,并实现先序中序后序层次遍历输出
【代码】C++代码实现中缀表达式转化为二叉树,并实现先序中序后序层次遍历输出。
【问题描述】
从标准输入中读入一个整数算术运算表达式,如 (3+4)*(8-2)+9/3=,构建表达式树,输出表达式树的前序、中序、后序以及层次遍历结果,并根据表达式树计算表达式的值。
【要求】
1. 表达式运算符只有+、-、*、/,表达式末尾的=字符表示表达式输入结束,表达式中可能会出现空格;
2. 表达式中会出现圆括号,括号可能嵌套,不会出现错误的表达式;
3. 出现除号/时,以整数相除进行运算,结果仍为整数,例如:5/3结果应为1。
4. 要求采用表达式树来实现表达式计算。
例如表达:(3+4)*(8-2)+9/3:
则生成的表达式树为:

【输入形式】
从键盘输入一个以=结尾的整数算术运算表达式。操作符和操作数之间可以有空格分隔。
【输出形式】
在屏幕上输出四种遍历结果(运算数后面加上#结束)以及计算结果(为整数,即在计算过程中除法为整除)。
【样例输入】
(3-9)/3+5-7*4=
【样例输出】
-+/-3#9#3#5#*7#4#
3#-9#/3#+5#-7#*4#
3#9#-3#/5#+7#4#*-
-+*/5#7#4#-3#3#9#
-25
现在我们逐段分析:
首先导入用到的库:
#include <iostream>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#define _CRT_SECURE_NO_WARNINGS//不弹出警告
using namespace std;
堆栈是用来将中缀表达式转化为后缀表达式,同时用于先中后序遍历表达树。队列是用来实现层次遍历。
此时我们遇到一个麻烦,使用string字符串中的字符元素来判断数字,如果使用str[i]>='0'&&str[i]<='9'这种方法,只能判断小于10的数字,局限性太强。不广泛适用。
此时我们可以自定义一个数据类型同时包含数字和字符类型,用flag来区分是数字还是字符。flag=true,表示操作数。
class node
{
public:
int num;
char op;
bool flag;
}
然后我们需要定义操作符栈,用于计算后缀表达式的整形元素的栈,后缀表达式队列,运算结果队列。
stack<node>s;//操作符栈
stack<int>st;//用于计算后缀表达式的栈
queue<node>q;//后缀表达式队列
queue<node>result;//运算结果队列
中缀转后缀代码:
void Infix2Sufix(string &str,queue<node>& tmp)//输入为字符串和结点队列指针
{
node tem;
//cout<<"中缀表达式为:"<<str<<endl;
string::size_type i;//string::size_type是一个无符号整数,用于表示字符串大小,即字符串的长度
for(i=0;i<str.length();i++)
{
//if(str[i]==' ')continue;
if(str[i]=='(')//node可以只指定一部分变量
{
tem.flag=false;
tem.op=str[i];
s.push(tem);
}
else if(str[i]=='=')continue;
else if(str[i]==')')
{
while(!s.empty() && s.top().op != '(')
{
q.push(s.top());//q表示后缀表达式队列
s.pop();//s是操作符栈
}
s.pop();
}
else if(str[i]>='0'&&str[i]<='9')
{
tem.flag=true;
tem.num = str[i] - '0';
int j=i+1;
if(str[j]>='0'&&str[j]<='9')
{
while(str[j]>='0'&&str[j]<='9')
{
tem.num=tem.num*10+str[j]-'0';//将字符串转换为数字
j++;i++;
}
}
q.push(tem);//q是一个既包含数据又包含运算符的结构的队列
}
else if(str[i]=='=')continue;
else
{
tem.flag=false;//表示操作符
while(!s.empty()&&p[str[i]]<=p[s.top().op])
{
q.push(s.top());
s.pop();
}
tem.op=str[i];//操作符入栈
s.push(tem);
}
}
while(!s.empty())
{
q.push(s.top());
s.pop();
}
//cout<<"后缀表达式为:";
while(!q.empty())
{
tmp.push(q.front());
result.push(q.front());
q.pop();
}
while(!result.empty())
{
if(result.front().flag==true)
{
st.push(result.front().num);
result.pop();
}
else if(result.front().flag==false)
{
if(result.front().op=='+')
{
int a1=st.top();
st.pop();
int a2=st.top();
st.pop();
st.push(a1+a2);
result.pop();
}
else if(result.front().op=='-')
{
int a1=st.top();
st.pop();
int a2=st.top();
st.pop();
st.push(a2-a1);
result.pop();
}
else if(result.front().op=='*')
{
int a1=st.top();
st.pop();
int a2=st.top();
st.pop();
st.push(a1*a2);
result.pop();
}
else if(result.front().op=='/')
{
int a1=st.top();
st.pop();
int a2=st.top();
st.pop();
st.push(a2/a1);
result.pop();
}
}
}
//return st.top();
}
生成表达树代码:
首先我们要定义一个叶子结点:
包含整形,字符型,布尔型(用于判断是数字还是字符),指针类型,分别指向左子树和右子树。构造函数用于初始化。
代码如下:
struct myNode
{
int num;//操作数
char op;//操作符
bool flag;//true表示操作数,false表示操作符
myNode* lchild;
myNode* rchild;
myNode(int d):num(d),lchild(nullptr),rchild(nullptr){};
myNode(char op):op(op),lchild(nullptr),rchild(nullptr){};
};
然后我们需要构造树:其实就是就两个作用,创造左子树和右子树,返回根节点。
代码如下:
class myTree
{
public:
myNode* root;
myTree():root(nullptr){};
void createTree(queue<node>tmp);
};
创造树就要用到我们刚刚产生的后缀表达式了。
下面是由后缀表达式构造表达树的过程。
定义一个由指向叶子结点的指针构成的栈,定义一个叶子结点指针(指针便于克隆,new)。遍历后缀表达式,如果后缀表达式中的元素是数字,则将其放进堆栈。如果后缀表达式中的元素是字符,则在堆栈中取出两个叶子结点作为其左右子结点。最后返回该树的根节点(可根据根节点访问所有节点)。
代码实现如下:
void myTree::createTree(queue<node>tmp)
{
stack<myNode*>s1; myNode*my_p;
while(!tmp.empty())//tmp表示后缀表达式(逆波兰表达式),其中元素是node结构体
{
if(tmp.front().flag==true)
{
my_p = new myNode(tmp.front().num);
my_p->flag = true;
s1.push(my_p);//s1是树的根节点
tmp.pop();
}
else if(tmp.front().flag==false)
{
my_p = new myNode(tmp.front().op);
my_p->flag = false;
my_p->rchild = s1.top();
s1.pop();
my_p->lchild = s1.top();
s1.pop();
s1.push(my_p);
tmp.pop();
}
}
root = s1.top();//构建一个树,返回该树的根节点即可
if(!s1.empty())
s1.pop();
}
还有一个小问题要解决,就是去除空格。
很显然,C++去除空格不如Python方便。以下是代码:
void removeSpace(string &str)
{
std::string::size_type index = 0;
if(!str.empty())
{
while((index=str.find(' ',index))!=string::npos)//使用str.find找出第一个空格位置,找到了,返回索引
//string::npos表示无效或不存在的字符串位置
{
str.erase(index,1);//删除字符串中index位置开始的第一个字符
}
}
}
到了表达树的遍历阶段了:根据先序遍历是根左右,中序遍历是左根右,后序遍历是左右根。
但是每一种遍历都要有两行这样的代码:if(root==nullptr)return;
这是根节点:
if(root->flag==true)
{
cout<<root->num<<"#";
}
else
{
cout<<root->op;
}
这是左子树节点:
postOrder(root->lchild);
这是右子树节点:
postOrder(root->rchild);
那么这三种遍历的代码如下:
void preOrder(myNode* root)
{
if(root==nullptr)return;
if(root!=nullptr)//先输出根结点的值
{
if(root->flag==true)
{
cout<<root->num<<"#";
}
else
{
cout<<root->op;
}
preOrder(root->lchild);//再遍历左节点
preOrder(root->rchild);//最后遍历右结点
}
}
//中序遍历,左中右
void inOrder(myNode* root)
{
if(root==nullptr)return;
if(root!=nullptr)
{
inOrder(root->lchild);//先遍历左节点
if(root->flag==true)//输出根节点的值
{
cout<<root->num<<"#";
}
else
{
cout<<root->op;
}
inOrder(root->rchild);//最后遍历右结点
}
}
//后序遍历,左右根
void postOrder(myNode* root)
{
if(root==nullptr)return;
if(root!=nullptr)
{
postOrder(root->lchild);//先遍历左节点
postOrder(root->rchild);//再遍历右结点
if(root->flag==true)//最后输出根节点的值
{
cout<<root->num<<"#";
}
else
{
cout<<root->op;
}
}
}
当然,层析遍历就有点不一样了,层次遍历是通过队列实现的,根节点进队,根节点出队,根节点的左右子结点进队,左子结点出队,左子节点的左右子结点进队,右子节点出队,右子节点的左右子结点进队...实现宽度优先搜索。
void levelOrder(myNode* root)
{
queue<myNode*>qmp;
qmp.push(root);
while(!qmp.empty())//实现宽度优先搜索,根节点出队,左右节点进队
{
myNode* pil = qmp.front();
qmp.pop();
if(pil->flag==true)
{
cout<<pil->num<<"#";
}
else
{
cout<<pil->op;
}
if(pil->lchild!=nullptr)
qmp.push(pil->lchild);
if(pil->rchild!=nullptr)
qmp.push(pil->rchild);
}
}
最后一步实现输出,代码如下:
int main(void)
{
//freopen("in.txt", "r", stdin);
//freopen("abc.out", "w", stdout);
string str;
p['+'] = p['-'] = 1;
p['*'] = p['/'] = 2;
//cin>>str;
getline(cin,str);
removeSpace(str);
int res=0;
queue<node>q_tmp ;
Infix2Sufix(str,q_tmp);
myTree * tree = new myTree;
tree->createTree(q_tmp);
preOrder(tree->root);
cout<<endl;
inOrder(tree->root);
cout<<endl;
postOrder(tree->root);
cout<<endl;
levelOrder(tree->root);
cout<<endl;
cout<<st.top();
return 0;
}
总代码如下:
#define _CRT_SECURE_NO_WARNINGS//不弹出警告
#include<iostream>
#include<stack>
#include<queue>
#include<string>
#include<vector>
#include<map>
#include<string>
#include<stdio.h>
//#include"infix2sufix.h"
using namespace std;
map<char,int> p;//一种关联容器,用于存储键值对,键是char字符,值是int整数
class node
{
public:
int num;//操作数
char op;//操作符
bool flag;//true表示操作数,false表示操作符,flag用于判断是否是操作数
};
stack<node>s;//操作符栈
stack<int>st;//用于计算后缀表达式的栈
queue<node>q;//后缀表达式队列
queue<node>result;//运算结果队列
void Infix2Sufix(string &str,queue<node>& tmp)//输入为字符串和结点队列指针
{
node tem;
//cout<<"中缀表达式为:"<<str<<endl;
string::size_type i;//string::size_type是一个无符号整数,用于表示字符串大小,即字符串的长度
for(i=0;i<str.length();i++)
{
//if(str[i]==' ')continue;
if(str[i]=='(')//node可以只指定一部分变量
{
tem.flag=false;
tem.op=str[i];
s.push(tem);
}
else if(str[i]=='=')continue;
else if(str[i]==')')
{
while(!s.empty() && s.top().op != '(')
{
q.push(s.top());//q表示后缀表达式队列
s.pop();//s是操作符栈
}
s.pop();
}
else if(str[i]>='0'&&str[i]<='9')
{
tem.flag=true;
tem.num = str[i] - '0';
int j=i+1;
if(str[j]>='0'&&str[j]<='9')
{
while(str[j]>='0'&&str[j]<='9')
{
tem.num=tem.num*10+str[j]-'0';//将字符串转换为数字
j++;i++;
}
}
q.push(tem);//q是一个既包含数据又包含运算符的结构的队列
}
else if(str[i]=='=')continue;
else
{
tem.flag=false;//表示操作符
while(!s.empty()&&p[str[i]]<=p[s.top().op])
{
q.push(s.top());
s.pop();
}
tem.op=str[i];//操作符入栈
s.push(tem);
}
}
while(!s.empty())
{
q.push(s.top());
s.pop();
}
//cout<<"后缀表达式为:";
while(!q.empty())
{
tmp.push(q.front());
result.push(q.front());
q.pop();
}
while(!result.empty())
{
if(result.front().flag==true)
{
st.push(result.front().num);
result.pop();
}
else if(result.front().flag==false)
{
if(result.front().op=='+')
{
int a1=st.top();
st.pop();
int a2=st.top();
st.pop();
st.push(a1+a2);
result.pop();
}
else if(result.front().op=='-')
{
int a1=st.top();
st.pop();
int a2=st.top();
st.pop();
st.push(a2-a1);
result.pop();
}
else if(result.front().op=='*')
{
int a1=st.top();
st.pop();
int a2=st.top();
st.pop();
st.push(a1*a2);
result.pop();
}
else if(result.front().op=='/')
{
int a1=st.top();
st.pop();
int a2=st.top();
st.pop();
st.push(a2/a1);
result.pop();
}
}
}
//return st.top();
}
struct myNode
{
int num;//操作数
char op;//操作符
bool flag;//true表示操作数,false表示操作符
myNode* lchild;
myNode* rchild;
myNode(int d):num(d),lchild(nullptr),rchild(nullptr){};
myNode(char op):op(op),lchild(nullptr),rchild(nullptr){};
};
class myTree
{
public:
myNode* root;
myTree():root(nullptr){};
void createTree(queue<node>tmp);
};
void myTree::createTree(queue<node>tmp)
{
stack<myNode*>s1; myNode*my_p;
while(!tmp.empty())//tmp表示后缀表达式(逆波兰表达式),其中元素是node结构体
{
if(tmp.front().flag==true)
{
my_p = new myNode(tmp.front().num);
my_p->flag = true;
s1.push(my_p);//s1是树的根节点
tmp.pop();
}
else if(tmp.front().flag==false)
{
my_p = new myNode(tmp.front().op);
my_p->flag = false;
my_p->rchild = s1.top();
s1.pop();
my_p->lchild = s1.top();
s1.pop();
s1.push(my_p);
tmp.pop();
}
}
root = s1.top();//构建一个树,返回该树的根节点即可
if(!s1.empty())
s1.pop();
}
//去除空格
void removeSpace(string &str)
{
std::string::size_type index = 0;
if(!str.empty())
{
while((index=str.find(' ',index))!=string::npos)//使用str.find找出第一个空格位置,找到了,返回索引
//string::npos表示无效或不存在的字符串位置
{
str.erase(index,1);//删除字符串中index位置开始的第一个字符
}
}
}
//先序遍历,根左右
void preOrder(myNode* root)
{
if(root==nullptr)return;
if(root!=nullptr)//先输出根结点的值
{
if(root->flag==true)
{
cout<<root->num<<"#";
}
else
{
cout<<root->op;
}
preOrder(root->lchild);//再遍历左节点
preOrder(root->rchild);//最后遍历右结点
}
}
//中序遍历,左中右
void inOrder(myNode* root)
{
if(root==nullptr)return;
if(root!=nullptr)
{
inOrder(root->lchild);//先遍历左节点
if(root->flag==true)//输出根节点的值
{
cout<<root->num<<"#";
}
else
{
cout<<root->op;
}
inOrder(root->rchild);//最后遍历右结点
}
}
//后序遍历,左右根
void postOrder(myNode* root)
{
if(root==nullptr)return;
if(root!=nullptr)
{
postOrder(root->lchild);//先遍历左节点
postOrder(root->rchild);//再遍历右结点
if(root->flag==true)//最后输出根节点的值
{
cout<<root->num<<"#";
}
else
{
cout<<root->op;
}
}
}
//层次遍历
void levelOrder(myNode* root)
{
queue<myNode*>qmp;
qmp.push(root);
while(!qmp.empty())//实现宽度优先搜索,根节点出队,左右节点进队
{
myNode* pil = qmp.front();
qmp.pop();
if(pil->flag==true)
{
cout<<pil->num<<"#";
}
else
{
cout<<pil->op;
}
if(pil->lchild!=nullptr)
qmp.push(pil->lchild);
if(pil->rchild!=nullptr)
qmp.push(pil->rchild);
}
}
int main(void)
{
//freopen("in.txt", "r", stdin);
//freopen("abc.out", "w", stdout);
string str;
p['+'] = p['-'] = 1;
p['*'] = p['/'] = 2;
//cin>>str;
getline(cin,str);
removeSpace(str);
int res=0;
queue<node>q_tmp ;
Infix2Sufix(str,q_tmp);
myTree * tree = new myTree;
tree->createTree(q_tmp);
preOrder(tree->root);
cout<<endl;
inOrder(tree->root);
cout<<endl;
postOrder(tree->root);
cout<<endl;
levelOrder(tree->root);
cout<<endl;
cout<<st.top();
return 0;
}
更多推荐


所有评论(0)