数据结构之二叉树----PTA题目7-20表达式转换(中缀转后缀)

一、前言

    上一篇文章 数据结构之栈----PTA题目7-20表达式转换(中缀转后缀)中,我们学会了利用堆栈将中缀表达式转化为缀表达式。今天我们换一种方式,通过 创建二叉树、遍历二叉树实现二元运算的中缀表达式转化为后缀表达式。事实上,当这个二叉树构造出来后,对其先序遍历可得前缀表达式,对其中序遍历可得中缀表达式,对其后序遍历可得后序表达式。一般的,在编译原理中,这种树的概念也是非常重要的,这也是一种一般的在给定文法后,对于字符串进行分析的方法。刷题中遇到的几乎所有的字符串分析题目都属于编译原理的内容,但我没正式学过编译原理。

二、题目

 

7-20 表达式转换(25 分)

算术表达式有前缀表示法、中缀表示法和后缀表示法等形式。日常使用的算术表达式是采用中缀表示法,即二元运算符位于两个运算数中间。请设计程序将中缀表达式转换为后缀表达式。

输入格式:

输入在一行中给出不含空格的中缀表达式,可包含+-*\以及左右括号(),表达式不超过20个字符。(编者注:最后一个应该是'/',原题就写错了)

输出格式:

在一行中输出转换后的后缀表达式,要求不同对象(运算数、运算符号)之间以空格分隔,但结尾不得有多余空格。

输入样例:

2+3*(7-4)+8/4

输出样例:

2 3 7 4 - * + 8 4 / +

三、测试样例

表达式转换测试用例
序号输入输出说明
02+3*(7-4)+8/42 3 7 4 - * + 8 4 / +正常测试6种运算符
1((2+3)*4-(8+2))/52 3 + 4 * 8 2 + - 5 /嵌套括号
21314+25.5*121314 25.5 12 * +运算数超过1位整数且有非整数出现
3-2*(+3)-2 3 *运算数前有正负号
4123123只有一个数字

 

 

 

 

 

 

注意:上一篇文章中我们测试得出结论,其实每个样例都是多位整数。

四、算法逻辑分析

1、构造二叉树

递归构造方法:先找到 最后计算的那个运算符(在表达式中最低优先级),然后左侧递归构造该运算符的左子树,右侧递归构造该运算符的右子树。
递归终止条件:当当前表达式仅含运算数,不含运算符时,递归终止。
特别注意:当仅含运算数时也可能含括号,因此应该去左右侧多余的括号才能得到运算数。

2、后序遍历

后序遍历二叉树并将结果存到一个字符串中。

3.决定成败的细节

重点:上边这些分析是个正常人都知道,问题的关键不在于此,而在于一些细节,这些细节有没有考虑才是决定能不能PASS的关键。

(1).何时创建节点申请内存,最后如何释放内存?

答:在定位出最低优先级运算符后,创建节点并赋值该运算符。

(2).如何定位最低优先级的运算符?

答:对于给定表达式,初始最低优先级为一个非常大的正数,基础优先级是0,遇到左括号基础优先级增加一个正数a(大于最高优先级);遇到右括号基础优先级减少一个正数a;遇到运算符比较基础优先级+运算符优先级是否小于等于当前记录的最低优先级,如果是则更新最低优先级算符位置和最低优先级大小。随后,创建子树。最后,如果最低优先级没被更新过,则说明没有运算符,只需要该子树的左右子树赋值为空,将给定表达式去除左右括号后赋值为节点字符串,否则,将最低优先级运算符赋值该节点字符串,递归创建左右子树。

(3).本题隐藏的测试点,正号需要去除,怎么去除?

读取建立二叉树时:

正负号的唯一标志是该符号是第一个字符或者该符号前一个字符是‘(’。在定位最低优先级运算符时,如果遇到正负号,直接跳过。

后序遍历时:

遇到叶子带正号的直接去除正号。

(4)如何去除最后一个空格?

最后用str.erase(len-1,1)函数去除最后一个空格就好了,这里不可以直接赋值为'\0',因为我使用的是string类型。

五、正确代码

#include <iostream>
#include <string>
#include <stdio.h>
#define STEP 3
#define MAX 9999999
using namespace std;

string str="";
struct Tree{
    string s;
    Tree *l,*r;
    Tree(){
        l=r=NULL;s="";
    }
    ~Tree(){
        delete l;
        delete r;
    }
};

int GetPri(char c){
    if(c=='+'||c=='-')return 1;
    if(c=='*'||c=='/')return 2;
    return -1;
}

void In2T(string s,Tree* &T){
    int i,id=-1;int len=s.length();
    int d=0;int p=-1;int mi=MAX;
    for(i=0;i<len;i++){///记录最后计算的那个运算符位置
        if(s[i]=='('){
            d+=STEP;
        }else if(s[i]==')'){
            d-=STEP;
        }else if(GetPri(s[i]==1)&&(i==0||s[i-1]=='(')){///正负号不是运算符
            continue;
        }else if((p=GetPri(s[i]))>0){
            if(p+d<=mi){
                mi=p+d;id=i;
            }
        }
    }
    T=new Tree;///创建子树
    if(id==-1){///去除左右括号
        int id1,id2;
        for(id1=0;id1<len&&s[id1]=='(';id1++);
        for(id2=len-1;id2>=0&&s[id2]==')';id2--);
        T->s=s.substr(id1,id2-id1+1);return;
    }
    T->s=s[id];///
    In2T(s.substr(0,id),T->l);///左子树
    In2T(s.substr(id+1,len-id-1),T->r);///右子树
}

void Post(Tree* T){
    if(!T)return;
    Post(T->l);
    Post(T->r);
    if(T->s[0]=='+'&&T->s.length()>1){///去除正号
        ///判断条件还可以为T->s[0]=='+'&&T->left==NULL&&T->right==NULL
       str=str+T->s.substr(1,T->s.length()-1)+" ";return;
    }
    str=str+T->s+" ";
}

int main()
{
    string ss;
    Tree* T=NULL;
    getline(cin,ss);
    In2T(ss,T);
    Post(T);
    int len=str.length();
    str.erase(len-1,1);///去除末尾空格
    cout<<str;
    return 0;
}

六、总结

同样的一个题目,不过这次我们不是机械的使用栈去完成任务,而是从编译原理的算符优先分析法出发,构造一棵树,这样不仅可以得到后序,前序也可以轻易得到。

 

  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值