華爲筆試,語法樹,用單行的類LISP語法字符串表示

有數字(0~9構成的正整數)、三種操作運算符(加法+、乘法*、自增^)、分隔符一個空格、左右括號

表達式形式是“(運算符 參數)”形式

比如(+ 3 4),求值結果7;(+ (* 2 3)(^4))求值結果11

語法樹結束後,後面加任何字符都是合法的,比如(+ (* 2 3)(^4)))))))#$是合法的

匆匆忙忙地寫了一個,感覺太長了。。。應該有很大的優化空間。

主要思路:用一個var類保存操作數,包括操作符和數字(存在聯合體中,用一個枚舉變量表示類型),然後遍歷輸入的字符串,將左括號、數字、操作符壓入計算棧,當遇到右括號時,彈出數字和操作符進行計算,並彈出左括號,將結果壓入。如果壓入結果之後,計算棧大小爲1,說明語法樹結束啦,直接跳出。中間任何操作失敗,都跳出輸出-1,主要是這個的判斷佔了很大篇幅,得想辦法優化。

#include <iostream>
#include <string>
#include <stack>
#include <unordered_set>
using namespace std;
struct var {
    union {
        int num;
        char op;
    };
    enum class type {ch,number} ty;
    var(int n, type t=type::number) :num(n), ty(t) {}
    var(char c, type t= type::ch) :op(c), ty(t) {}
};
using vtype = var::type;
unordered_set<char> ops = { '+','*','^' };
int main() {
    string exp;
    while (getline(cin,exp)) {
        stack<var> cal;
        bool valid =true;
        for (auto i = exp.cbegin(); i != exp.cend() && valid;++i) {
            if (*i >= '0' && *i <= '9') {//數字
                int num = *i++ - '0';
                while (i!=exp.cend() &&*i >= '0' && *i <= '9') {
                    num = num * 10 + *i++ - '0';
                }
                if (i == exp.cend()) {
                    valid = false;
                    break;
                }
                else
                    cal.emplace(num);
            }
            if (*i == '(' || ops.find(*i) != ops.end())//左括號或者操作符
                cal.emplace(*i);
            else if (*i == ')') {//有右括號就彈出東西來計算,如果不對就是不合法的
                bool dual;
                if (cal.empty() || cal.top().ty != vtype::number) {
                    valid = false;
                    break;
                }
                int rhs = cal.top().num;
                int lhs;
                int res;
                cal.pop();
                if (cal.empty()) {
                    valid = false;
                    break;
                }
                if (cal.top().ty == vtype::number) {
                    dual = true;
                    lhs = cal.top().num;
                    cal.pop();
                } else if (cal.top().op == '^') {
                    cal.pop();
                    res = ++rhs;
                    dual = false;
                } else {
                    valid = false;
                }
                if (dual) {
                    if (!cal.empty() && cal.top().ty == vtype::ch) {
                        char op = cal.top().op;
                        cal.pop();
                        if (op == '+')res = lhs + rhs;
                        else res = lhs * rhs;
                    } else {
                        valid = false;
                    }
                }
                if (!cal.empty() && cal.top().ty == vtype::ch && cal.top().op == '(') {
                    cal.pop();
                    cal.emplace(res);
                } else {
                    valid = false;
                }
                if (cal.size() == 1 && cal.top().ty == vtype::number)break;//語法樹結束
            } else if (*i == ' ')continue;
            else {
                if (cal.size() == 1 && cal.top().ty == vtype::number)//非法字符,但是語法樹已結束
                    break;
                else valid = false;
            }          
        }
        if (valid && cal.size() == 1 && cal.top().ty == vtype::number) {
                cout << cal.top().num << endl;
        }
        else cout << -1<<endl;
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章