表达式树创建与计算(中缀表达式转逆波兰式)

题目描述

给定一串计算表达式

例如 (1+2*4-2)*3+4 * (2+4)/2

计算此表达式的值,并输出此表达式树的前序中序后序序列。

题解思路

首先,给定的表达式为正常逻辑的中缀表达式,我们需要将其转化为逆波兰式(后缀表达式),如下给出理由

例如 a*b-c

它的二叉树如下
在这里插入图片描述
前序序列为:- * a b c
中序序列为: a * b - c
后序序列为: a b * c -

对于前序序列,运算顺序应该为首先对离运算符最近的ab操作,ab前有乘号,将ab相乘,代替ab的位置,然后此时离运算符最近的为ab的结果与c,前方的操作符为-号则进行a*b-c操作。而此种运算顺序实际上不利于代码模拟。

相反,对于后序序列,则可以用栈轻松模拟,首先创建一个操作数栈,从左到右遍历。

  • 首先为a,压入操作数栈
  • 对b来说,压入操作数栈
  • 对*来说,取出操作数栈中栈顶元素和次栈顶元素b,a并将这两个元素pop操作。将b,a进行相乘后重新压入操作数栈
  • 对c来说,压入操作数栈
  • 对-来说,取出操作数栈中栈顶元素和次栈顶c和a*b,将这两个元素进行先相减操作,重新压入栈。(注意为次栈顶减栈顶)
  • 结束后栈中剩下的一个数则为表达式的值

因此我们想要计算表达式,可以先将其转化成逆波兰式(后缀表达式),就可以计算出值,而对于如何从中缀表达式转成后缀表达式,思路如下:

首先创建一个运算符栈,从左到右遍历,利用vector存储答案

  • 如果为操作数,直接压入vector
  • 如果为运算符
    • 如果此时运算符栈为空,直接压入运算符栈
    • 如果运算符为(,左括号的话,直接压入运算符栈
    • 如果此时运算符为),右括号的话,不断将运算符栈顶元素压入vector,直到匹配到(,左括号为止(注意:左右括号都不进入vector)
    • 如果此时运算符优先级大于栈顶元素,直接压入运算符栈
    • 如果运算符优先级小于等于栈顶元素,不断将运算符栈顶元素压入vector,直到栈空或者栈顶元素优先级小于此运算符,然后将此运算符压入运算符栈。

vector中即为后缀表达式。
对于正确性,可以自己写几个例子简单模拟一下。

解决了从中缀转后缀,就可以算出值,而对于通过后缀表达式建立二叉树,思路与后缀表达式计算一样。

思路如下:(从左到右遍历后缀表达式)
首先创建一个指针栈

  • 如果此时为操作数,创建指针指向此数,将指针压入栈
  • 如果此时为操作符,创建指针指向此操作符,再将栈顶指针和次栈顶指针拿出,将此操作符指针的左孩子指针指向原次栈顶指针,右孩子指针指向原栈顶指针。再将此操作符指针压入栈中。

结束后栈中剩下的指针即为此二叉树的根结点指针。

代码如下

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <stack>
#include <vector>
using namespace std;
const int N=100010;
typedef struct node
{
    char data[1000];
    struct node *lchild;
    struct node *rchild;
}node,*root;
string str,str1;
vector<string>res;
int ans=0;
int fun2(string a)//运算符优先级判断
{
    if(a[0]=='+'||a[0]=='-')return 0;
    if(a[0]=='*'||a[0]=='/')return 1;
    if(a[0]=='(')return -1;
}
bool cmp(string a,string b)
{
    int x=fun2(a),y=fun2(b);
    if(x<y)return 1;
    return 0;
}
void fun1()//中缀表达式转后缀表达式
{
    vector<string>v;
    string p="";
    for(int i=0;i<str.size();i++)//对字符串切片处理
    {
        if(str[i]>='0'&&str[i]<='9')
        {
            p+=str[i];
        }
        else
        {
            if(p.size()!=0)v.push_back(p);
            p=str[i];
            v.push_back(p);
            p="";
        }
    }
    if(p.size())v.push_back(p);
    stack<string>s;
    for(int i=0;i<v.size();i++)
    {
        if(v[i][0]>='0'&&v[i][0]<='9')res.push_back(v[i]);
        else
        {
            if(!s.size()||v[i][0]=='(')s.push(v[i]);
            else
            {
                if(v[i][0]==')')
                {
                    while(s.size())
                    {
                        string now=s.top();
                        s.pop();
                        if(now[0]=='(')break;
                        res.push_back(now);
                    }
                }
                else
                {
                    if(cmp(s.top(),v[i]))s.push(v[i]);
                    else
                    {
                        while(s.size()&&!cmp(s.top(),v[i]))
                        {
                            res.push_back(s.top());
                            s.pop();
                        }
                        s.push(v[i]);
                    }
                    
                }
                
            }
            
        }
        
    }
    while(s.size())
    {
        res.push_back(s.top());
        s.pop();
    }
    //for(int i=0;i<v.size();i++)cout<<v[i]<<endl;
}
root build()//二叉树建立
{
    stack<root>s;
    for(int i=0;i<res.size();i++)
    {
        node *no=(root)malloc(sizeof(node));
        //cout<<"yes";
        //cout<<res[i]<<endl;
        if(res[i][0]>='0'&&res[i][0]<='9')
        {
            strcpy(no->data,res[i].c_str());
            //cout<<no->data<<endl;
            no->lchild=NULL;
            no->rchild=NULL;
            s.push(no);
        }
        else
        {
            strcpy(no->data,res[i].c_str());
            no->rchild=s.top();
            s.pop();
            no->lchild=s.top();
            s.pop();
            s.push(no);
        }
    }
    return s.top();
}
int js()//计算表达式的值
{
    stack<int>s;
    for(int i=0;i<res.size();i++)
    {
        if(res[i][0]>='0'&&res[i][0]<='9')
        {
            //cout<<"YES";
            string temp=res[i];
            //cout<<temp<<endl;
            int tt=0;
            for(int j=0;j<temp.size();j++)
            {
                tt=tt*10+temp[j]-'0';
            }
            s.push(tt);
            //cout<<tt<<endl;
        }
        else
        {
            int a=s.top();
            s.pop();
            int b=s.top();
            s.pop();
            int temp=0;
            if(res[i][0]=='+')temp=a+b;
            else if(res[i][0]=='-')temp=b-a;
            else if(res[i][0]=='*')temp=a*b;
            else temp=b/a;
            s.push(temp);
            //cout<<temp<<endl;
        }
    }
    return s.top();
}
void pre(root roo)//前序遍历
{
    if(roo!=NULL)
    {
        cout<<roo->data;
        pre(roo->lchild);
        pre(roo->rchild);
    }
}
void mid(root roo)//中序遍历
{
    if(roo!=NULL)
    {
        mid(roo->lchild);
        cout<<roo->data;
        mid(roo->rchild);
    }
}
void back(root roo)//后序遍历
{
    if(roo!=NULL)
    {
        back(roo->lchild);
        back(roo->rchild);
        cout<<roo->data;
    }
}
int main(void)
{
    cin>>str;
    fun1();
    /* for(int i=0;i<res.size();i++)
    {
        cout<<res[i];
    } 
    cout<<endl; */
    root roo=build();//(1+2*4-2)*3+4*(2+4)/2
    ans=js();
    pre(roo);
    cout<<endl;
    mid(roo);
    cout<<endl;
    back(roo);
    cout<<endl;
    cout<<ans<<endl;
    //system("pause");
    return 0;
}
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理的输入是一个数学表达式,目的是将该表达式换为逆波兰式m作为输出逆波兰式,又称为后缀表达式,是一种将操作符写在操作数之后的数学表达式。与常见的中缀表达式相比,逆波兰式更方便计算机处理和计算。 编译原理在处理输入表达式时,首先将表达式进行词法分析,将表达式中的字符序列分解为不同的单词,例如操作数或操作符。接着进行语法分析,根据语法规则将这些单词组合成语法。 然后,编译原理使用逆波兰式换算法将语法换为逆波兰式m。该算法利用来实现表达式换。遍历语法结点的顺序是从根结点开始,先遍历左子,再遍历右子,最后处理根结点。 具体换过程如下: 1. 若当前结点是操作数,则将其加入逆波兰式m中。 2. 若当前结点是操作符,则将其加入逆波兰式m中,并将当前结点入。 3. 若当前结点是右括号,则弹出中的操作符加入逆波兰式m中,直到遇到左括号为止,将左括号从中弹出。 4. 若当前结点是其他操作符(加减乘除等),则比较其与顶操作符的优先级。若当前操作符优先级较低,则将顶操作符出,并加入逆波兰式m中,然后将当前操作符入。若当前操作符优先级较高或相等,则直接入。 5. 遍历完整个语法后,将中剩余的操作符出并加入逆波兰式m中。 最终,经过以上算法处理,逆波兰式m生成完成,作为编译原理的输出。这样,我们可以利用逆波兰式m进行数学表达式计算
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值