表达式树的简析

二叉树是表达式处理的常用工具。找到"最后计算"的运算符(它是整棵表达式树的根),然后递归处理。
递归处理成树是表达式处理的一个重要方式。这里我只记录和理解了lrjlrjlrj老师的代码(抄了一遍,自己还没动手自己写过。不过目前这不是重点内容,所以到此为止。
个位数加减乘除成表达式树

const int maxn = 1000;
int lch[maxn],rch[maxn];char op[maxn];
int nc=0;

int build_tree(char* s,int x,int y){
    int i,c1=-1,c2=-1,p=0;
    int u;
    if(y-x==1){
        u=++nc;
        lch[u]=rch[u]=0;op[u]=s[x];
        return u;
    }
    for(int i=x;i<y;i++){
        switch(s[i]){
            case '(':p++;break;
            case ')':p--;break;
            case '+':case '-':if(!p)c1=i;break;
            case '*':case '/':if(!p)c2=i;break; 
        }
    }//找当前表达式的最后计算的运算符(肯定不在最后,并且在括号外
    if(c1<0)c1=c2;//找不到括号外的加减号,用乘除号
    if(c1<0)return build_tree(s,x+1,y-1);//找不到其他符号:整个表达式被一对括号括起来
    u=++nc;
    lch[u]=build_tree(s,x,c1);
    rch[u]=build_tree(s,c1+1,y);
    op[u]=s[c1];
    return u;
}

引入一道例题,UVa12219UVa12219UVa12219
对于一棵二叉树(字符串作为节点)的括号表达式,你需要简化这个表达式,使得相同子树返回子树编号。
这里采用递归建树,用mapmapmap记录子树编号,如果存在就使得新建子树编号减一。
最后递归输出。lrjlrjlrj老师真的tqltqltql

#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<map>
#include<vector>
#include<set>
#include<queue>
#include<cctype>
#include<string>
#include<cmath>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

#define ll long long
#define inf 0x3f3f3f3f3f3f3f3f
#define eps 1e-6

const int maxn = 60000;
int T,kase,cnt;
char expr[maxn*5],*p;//用于遍历
int done[maxn];//结点是否已经输出

struct Node{
    string s;
    int hash,left,right;
    bool operator < (const Node& rhs) const{
        if(hash!=rhs.hash)return hash < rhs.hash;
        if(left!=rhs.left)return left < rhs.left;
        return right<rhs.right;
    }
}node[maxn];

map<Node,int>dict;

int parse(){
    int id = cnt++;//第几棵子树(相同的话后面会-1
    Node& u = node[id];//对于新树如果是原来的子树,下次创新的时候用的还是这个cnt(后面-1了)
    u.left=u.right=-1;
    u.s="";
    u.hash=0;
    while(isalpha(*p)){
        u.hash=u.hash*27+*p-'a'+1;//27进制哈希
        u.s.push_back(*p);
        p++;
    }
    if(*p=='('){
        p++;u.left=parse();p++;u.right=parse();p++;
    }//递归处理括号内容,注意左右括号和中间的逗号要跳过
    if(dict.count(u)!=0){
        id--;cnt--;
        return dict[u];//返回:map上的对应子树结点
    }
    return dict[u] = id;//未重复。
}

void print(int v){
    if(done[v] == kase){
        printf("%d",v+1);
    }
    else{
        done[v]=kase;
        printf("%s",node[v].s.c_str());
        if(node[v].left!=-1){
            putchar('(');
            print(node[v].left);
            putchar(',');
            print(node[v].right);
            putchar(')');
        }
    }
}

int main(){
    scanf("%d",&T);
    for(kase = 1;kase <= T;kase ++){
        dict.clear();
        cnt = 0;
        scanf("%s",expr);
        p = expr;
        print(parse());
        putchar('\n');
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值