表达式求值(中缀表达式——后缀表达式——求值)

        表达式求值,属于数据结构——的典型应用。使用后缀表达式的与原因,是因为在求值的过程中,不需要考虑操作符的优先级。(结合性仍需要考虑)

        但是一般的书上只讲到如何处理二元操作符,并且结合性都是从左到右结合的。这里的实现能够处理一元操作符,并且能够处理从右向左结合的幂乘'^'操作符。

功能需求

        给定一个中缀表达式,求出该表达式的值。
        要求支持“加减乘除 + - * /、取余 %、幂运算 ^、一元正负号 + -、括号 ()、数的科学表示法符号 E e”,其中含有一元操作符'+ -',也含有从右向左结合性的操作符'^',注意操作数的位数可以为多位。

分析

        需要考虑三个方面:① 优先级; ② 结合性; ③ 几元操作符;

        优先级:若操作符的优先级不同,那么先计算优先级高的。

        结合性:若操作符的优先级相同,那么先计算优先级的结合性决定求值顺序。

        几元操作符:一元和二元操作符,决定了参与求值的操作数的个数。

处理方法

        1. 利用数字0,将一元操作转化为二元操作,例如将"-3"转化为"0-3";

        2. 将操作符按照优先级分类,确定各个操作符的优先级;

        3. 结合性不同的操作符,由于数量较少(这里只有'^'),处理时单独判断;

具体步骤

        1. 判断是否有不匹配的括号

        2. 预处理中缀表达式
                2.1 判断是否有不能处理的字符;
                2.2 去除所有的空格;
                2.3 处理一元操作符'+'和'-':
                        2.3.1 如果是在开始位置,在开始处添加"0";
                        2.3.2 如果是在“非数字字符”(右括号‘)’除外)之后,那么先在一元操作符前插入"(0",然后在一个“完整的数字”或者“括号后面”添加右括号")";

        3. 中缀转化为后缀表达式 (利用“操作符栈”)
            遍历中缀表达式:
                3.1 如果是操作数,读取其所有的位,然后进入后缀表达式队列;
                3.2 如果是操作符( + – * / ^ % )
                        3.2.1 如果“操作符栈”为空,直接入栈;
                        3.2.2 如果当前操作符优先级>栈顶元素优先级,那么入栈;
                        3.2.3 如果当前操作符优先级<栈顶元素优先级,那么栈顶操作符出栈,循环执行;
                        3.2.4 如果当前操作符优先级=栈顶元素优先级,如果当前元素是右结合性,那么入栈;否则,栈顶元素出栈;循环执行。
                3.3 如果是左括号'(',直接入栈
                3.4 如果是右括号')',如果栈非空,那么栈顶元素出栈,直到遇到左括号'(';
                3.5 遍历结束中,将操作符栈中的元素依次出栈,添加到后缀表达式队列中。
        4. 计算后缀表达式
            从后缀表达式队列中依次取出元素
                4.1 如果是操作数,那么将其压入“结果栈”中;
                4.2 如果是操作符,从“结果栈”中取出两个元素,进行计算。(注意从栈中取元素的顺序和操作数的顺序是相反的)
                遍历后缀表达式结束后,“结果栈”中的元素就是最终的结果。

具体代码

/**
 * 中缀表达式-->后缀表达式-->表达式求值
 *
 */
#include <cstdlib>
#include <iostream>
#include <string>
#include <cctype>
#include <set>
#include <queue>
#include <stack>
#include <map>
#include <cmath>
using namespace std;

/**
 * 判断是否为操作符
 * 参数:
 *      ch : 待判断的字符
 * 返回值:
 *      是操作符,返回true;否则返回false;
 */
bool is_operator(char ch) {
    set<char> operator_set;
    operator_set.insert('+');
    operator_set.insert('-');
    operator_set.insert('*');
    operator_set.insert('/');
    operator_set.insert('%');
    operator_set.insert('^');
    return operator_set.find(ch) != operator_set.end();
}
/**
 * 比较两个操作符的优先级
 */
int compare_priority(char a, char b) {
    map<char, int> operator_priority;
    operator_priority.insert(make_pair('+', 1));
    operator_priority.insert(make_pair('-', 1));
    operator_priority.insert(make_pair('*', 2));
    operator_priority.insert(make_pair('/', 2));
    operator_priority.insert(make_pair('%', 2));
    operator_priority.insert(make_pair('^', 3));

    return operator_priority[a]-operator_priority[b];
}
/**
 * 判断是否有不匹配的括号
 */
bool is_bracket_valid(string infix) {
    stack<char> bracket;
    for(int i=0; i<infix.size(); i++) {
        if(infix[i]=='(') {
            bracket.push(infix[i]);
        } else if(infix[i]==')') {
            if(bracket.empty()) {
                cout<<"有右括号没有匹配"<<endl;
                return false;
            }
            bracket.pop();
        }
    }
    if(!bracket.empty()) {
        cout<<"有左括号没有匹配"<<endl;
        return false;
    }
    return true;
}
/**
 * 预处理
 * 参数:
 *      infix:预处理前的中序表达式
 * 返回值:
 *      处理后的中序表达式
 * 步骤:
 *      1. 去除空格
 *      2. 判断是否含有不能处理的字符
 *      3. 处理一元操作符 '+'和'-'
 */
string preprocess(const string infix) {
    string result = infix;
    //去除空格
    size_t index;
    while((index = result.find(string(" "))) != string::npos) {
        result.erase(index, 1);
    }
    cout<<"去除空格后  "<<result<<""<<endl;

    //初始化能够处理的字符集合
    set<char> valid_char_set;//能够处理的字符
    for(int i=0; i<=9; i++) {
        valid_char_set.insert(i+'0');
    }
    valid_char_set.insert('+');
    valid_char_set.insert('-');
    valid_char_set.insert('*');
    valid_char_set.insert('/');
    valid_char_set.insert('%');
    valid_char_set.insert('^');
    valid_char_set.insert('(');
    valid_char_set.insert(')');
    valid_char_set.insert('e');//'e'和'E'为科学计数法
    valid_char_set.insert('E');
    valid_char_set.insert('.');//小数点

    for(int i=0; i<result.size(); i++) {
        //如果字符不在valid_char_set,说明有不能处理的字符,结束程序
        if(valid_char_set.find(result[i]) == valid_char_set.end()) {
            cout<<"中缀表达式中有非法字符: "<<result[i]<<"  "<<i<<endl;
            exit(-1);
        }
    }

    //处理一元操作符
    for(int i=0; i<result.size(); i++) {
        char temp = result[i];
        if(temp != '+' && temp != '-') {
            continue;
        }
//        cout<<i<<"   "<<result<<"   "<<result[i]<<endl;
        if(i==0) { //表达式开始位置的 '-'
            result.insert(i, 1, 0+'0');
        } else if(i-1>=0 && !isdigit(result[i-1]) && result[i-1]!=')') { //一元+-,紧跟着其他符号后面
            result.insert(i, "(0");
            int j = i+3;
            int bracket_count=0;//如果有括号,应包含括号
            for(; j<result.size(); j++) {
                if(isdigit(result[j]) || result[j]=='.') {
                    continue;
                } else if(result[j]=='(') {
                    ++bracket_count;
                } else if(result[j]==')') {
//                    --left_bracket_count;
                    if((--bracket_count) == 0) {
                        break;
                    }
                } else if(bracket_count==0) {
                    break;
                }
            }
            i = j;
            result.insert(j, ")");
        }
    }
    return result;
}
/**
 * 中缀表达式-->后缀表达式
 */
queue<string> infix_to_post(string infix) {
    queue<string> postfix;//后缀表达式队列
    stack<char> operator_stack;//转换过程中,用来存储操作符的栈

    set<char> valid_operand_set;//操作数 中的字符
    for(int i=0; i<=9; i++) {
        valid_operand_set.insert(i+'0');
    }
    valid_operand_set.insert('.');
    valid_operand_set.insert('e');
    valid_operand_set.insert('E');

    for(int i=0; i<infix.size(); i++) {
        cout<<endl;
        char ch = infix[i];
        cout<<"序号:"<<"  "<<i<<"  字符:  "<<ch<<endl;
        if(infix[i]=='(') {//左括号
            operator_stack.push(infix[i]);
        } else if(infix[i]==')') {//右括号
            while(!operator_stack.empty() && operator_stack.top()!='(') {
                postfix.push(string(1, operator_stack.top()));
                operator_stack.pop();
            }
            operator_stack.pop();//将"("出栈
        } else if(is_operator(infix[i]) == true) { //是操作符(不包含 左、右括号)
            if(operator_stack.empty()) { //操作符栈为空
                operator_stack.push(infix[i]);
                continue;
            }

            //操作符栈非空
            char top_stack = operator_stack.top();
            //将栈中“较高和相等”优先级的操作符放到 后缀表达式中。
            //终止条件为:当前操作符>栈顶操作符优先级,或优先级相等、但栈顶操作符的结合性是“从右向左”。
            while(compare_priority(infix[i], top_stack)<=0) {
                //优先级相等,但栈顶操作符结合性为从右向左,这里特指'^'
                if(compare_priority(infix[i], top_stack)==0 && infix[i]=='^') {   //因为'^'的结合性从右向左,所以单独处理
                    break;
                }
                //当前操作符<=栈顶操作符优先级,当前操作符结合性为从左到右
                postfix.push(string(1, top_stack));
                operator_stack.pop();
                if(!operator_stack.empty()) {
                    top_stack = operator_stack.top();
                } else {
                    break;
                }

            }
            //将当前操作符入栈
            operator_stack.push(infix[i]);
        } else {//操作数
            string current_operator;
            int j=i;
            while(valid_operand_set.find(infix[j]) != valid_operand_set.end()) {
                current_operator += infix[j];
                ++j;
            }
            postfix.push(current_operator);
            i=j-1;//因为for循环,每次都会执行i++
        }
        
        //打印处理过程
        cout<<"当前栈顶: "<<(operator_stack.empty() ?' ': operator_stack.top())<<endl;
        queue<string> temp_queue = postfix;
        cout<<"当前后缀表达式: ";
        while(temp_queue.size()>0) {
            cout<<temp_queue.front()<<"  ";
            temp_queue.front();
            temp_queue.pop();
        }
        cout<<endl;

    }
    //最后将栈中内容全部取出来
    while(!operator_stack.empty()) {
        postfix.push(string(1, operator_stack.top()));
        operator_stack.pop();
    }

    return postfix;
}
/**
 * 计算两个操作数
 */
double calculate_two(double a, double b, string operand) {
    double result;
    if(operand == "+") {
        result = a+b;
    } else if(operand == "-") {
        result = a-b;
    } else if(operand == "*") {
        result = a*b;
    } else if(operand == "/") {
        if(b==0) {
            cout<<"除数不能为0"<<endl;
            exit(-1);
        }
        result = a/b;
    } else if(operand == "%") {
        result = (static_cast<int>(a)) % (static_cast<int>(b));
    } else if(operand == "^") {
        result = pow(a, b);
    }
    return result;
}
//对后缀表达式,进行计算
double calculate_post(queue<string>& post) {
    stack<double> result_stack;
    while(!post.empty()) {
        string temp = post.front();
        post.pop();
        if(is_operator(temp[0])) { //是操作符
            if(result_stack.size()<2) {
                cout<<"表达式错误"<<endl;
                exit(-1);
            }
            //从栈中取出两个元素,计算并将结果压入栈中
            double operand2 = result_stack.top();
            result_stack.pop();
            double operand1 = result_stack.top();
            result_stack.pop();

            double m = calculate_two(operand1, operand2, temp);
            result_stack.push(m);
        } else { //操作数
            double temp_operand = atof(temp.c_str());
            result_stack.push(temp_operand);
        }
    }
    return result_stack.top();
}

int main(int argc, char **argv) {
    string infix;
//    infix = "10e2+3*4-5%2-2^-(4/2)+.2 + 2^1^3";//结果为:1012.95
    cout<<"请输入中缀表达式: "<<endl;
    getline(cin, infix);
    cout<<endl<<"原始前缀表达式: "<<infix<<endl;

    //1. 检测括号是否匹配
    bool valid = is_bracket_valid(infix);
    if(valid){
        cout<<endl<<"括号匹配"<<endl<<endl;
    }else{
        return 0;
    }
    
    //2.预处理中缀表达式
    string result_infix = preprocess(infix);
    cout<<"预处理以后:"<<result_infix<<endl;

    //3. 中缀 转 后缀
    queue<string> result_post = infix_to_post(result_infix);
    //打印后缀表达式
    queue<string> temp = result_post;
    cout<<"后缀表达式: ";
    while(!temp.empty()) {
        cout<<temp.front()<<"  ";
        temp.pop();
    }
    cout<<endl;
    
    //4. 计算结果
    double result = calculate_post(result_post);
    cout<<endl<<"计算结果: "<<result<<endl;
    
    return 0;
}

运行结果:

请输入中缀表达式:
10e2+3*4-5%2-2^-(4/2)+.2 + 2^1^3

原始前缀表达式: 10e2+3*4-5%2-2^-(4/2)+.2 + 2^1^3

括号匹配

去除空格后  10e2+3*4-5%2-2^-(4/2)+.2+2^1^3
预处理以后:10e2+3*4-5%2-2^(0-(4/2))+.2+2^1^3

序号:  0  字符:  1
当前栈顶:
当前后缀表达式: 10e2

序号:  4  字符:  +

序号:  5  字符:  3
当前栈顶: +
当前后缀表达式: 10e2  3

序号:  6  字符:  *
当前栈顶: *
当前后缀表达式: 10e2  3

序号:  7  字符:  4
当前栈顶: *
当前后缀表达式: 10e2  3  4

序号:  8  字符:  -
当前栈顶: -
当前后缀表达式: 10e2  3  4  *  +

序号:  9  字符:  5
当前栈顶: -
当前后缀表达式: 10e2  3  4  *  +  5

序号:  10  字符:  %
当前栈顶: %
当前后缀表达式: 10e2  3  4  *  +  5

序号:  11  字符:  2
当前栈顶: %
当前后缀表达式: 10e2  3  4  *  +  5  2

序号:  12  字符:  -
当前栈顶: -
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -

序号:  13  字符:  2
当前栈顶: -
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2

序号:  14  字符:  ^
当前栈顶: ^
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2

序号:  15  字符:  (
当前栈顶: (
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2

序号:  16  字符:  0
当前栈顶: (
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0

序号:  17  字符:  -
当前栈顶: -
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0

序号:  18  字符:  (
当前栈顶: (
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0

序号:  19  字符:  4
当前栈顶: (
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0  4

序号:  20  字符:  /
当前栈顶: /
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0  4

序号:  21  字符:  2
当前栈顶: /
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0  4  2

序号:  22  字符:  )
当前栈顶: -
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0  4  2  /

序号:  23  字符:  )
当前栈顶: ^
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0  4  2  /  -

序号:  24  字符:  +
当前栈顶: +
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0  4  2  /  -  ^  -

序号:  25  字符:  .
当前栈顶: +
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0  4  2  /  -  ^  -  .2

序号:  27  字符:  +
当前栈顶: +
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0  4  2  /  -  ^  -  .2  +

序号:  28  字符:  2
当前栈顶: +
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0  4  2  /  -  ^  -  .2  +  2

序号:  29  字符:  ^
当前栈顶: ^
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0  4  2  /  -  ^  -  .2  +  2

序号:  30  字符:  1
当前栈顶: ^
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0  4  2  /  -  ^  -  .2  +  2  1

序号:  31  字符:  ^
当前栈顶: ^
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0  4  2  /  -  ^  -  .2  +  2  1

序号:  32  字符:  3
当前栈顶: ^
当前后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0  4  2  /  -  ^  -  .2  +  2  1  3
后缀表达式: 10e2  3  4  *  +  5  2  %  -  2  0  4  2  /  -  ^  -  .2  +  2  1  3  ^  ^  +

计算结果: 1012.95

Press any key to continue.

本文链接:http://blog.csdn.net/daheiantian/archive/2011/03/19/6553713.aspx

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值