简单算术表达式C++实现

来源:互联网 发布:算法与并行计算 编辑:程序博客网 时间:2024/06/15 11:35

输入的算术表达式是中缀表达式,由于操作符的优先级不同,不能直接进行求解,而后缀表达式的计算不需要判别操作符的优先级,所以我实现算术表达式的思路是:1)应用堆栈将中缀表达式转化为后缀表达式,2)用堆栈实现后缀表达式求值。
用堆栈实现后缀表达式求值的过程:从左到右读入后缀表达式的各项,并根据读入的对象判断执行何种操作,有以下3中情况:
1) 当读入的是一个运算数时,把它压入栈中;
2) 当读入的是一个运算符时,就从栈中弹出适当数量的运算数并进行计算,计算结果再压回到栈中;
3) 处理完整个后缀表达式之后,栈顶上的元素就是表达式的结果。
应用堆栈将中缀表达式转换为后缀表达式的基本过程为:从头到尾读取中缀表达式的每个对象,对不同对象按不同情况处理:
1) 如果遇到空格则认为是分隔符,无需处理;
2) 若遇到运算数,则直接输出;
3) 若是左括号,则将其压入至栈中;
4) 若遇到的是右括号,表明括号内的中缀表达式已经扫描完毕,将栈顶的运算符弹出并输出,直到遇到左括号(左括号也出栈,但不输出);
5) 若遇到的是运算符,若该运算符的优先级大于栈顶运算符的优先级,则把它压入栈中;若该运算符的优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出,在比较新的栈顶运算符,按这样的处理方法,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈;
6) 若中缀表达式中的各对象处理完毕,则把堆栈中保留的运算符一并输出。
在代码实现的过程中,首先我们要定义一个栈,因为在后缀表达式求值的过程中,栈是用来存运算数的;在中缀转换为后缀表达式的过程中,栈是用来存放操作符的。所以在实现栈的时候我用了模板。栈的实现如下:
// Stack.h

#include<vector>template <class T>class Stack{public:    void push(const T&);    T pop();    T peek();//该函数只是返回最顶端的元素,并不删除    bool empty();private:    std::vector<T> _stack;};template<class T>void Stack<T>::push(const T& a){    _stack.push_back(a);}template<class T>T Stack<T>::pop(){    //在要用到出栈的时候一定要保证栈不为空    auto it = _stack.end();    auto c = *(it - 1);    _stack.pop_back();    return c;}template<class T>bool Stack<T>::empty(){    return _stack.begin() == _stack.end();}template<class T>T Stack<T>::peek(){    auto it = _stack.end();    return *(it - 1);}

定义相应的运算符的优先级,我用整数的大小来定义优先级的大小,对应的函数实现如下:

int op_pri(const char& a){    switch (a){    case ')':        return 0;    case '+':case '-':        return 1;    case '*':case'/':        return 2;    case '(':        return 3;    }}

在实现优先级的时候,当输入为左括号时,需要无条件进栈,所以左括号的优先级要最高。碰到右括号时要无条件出栈直到那个栈顶的操作符是左括号。所以右括号的优先级我定义为最低。但是要注意当当前的运算符为右括号时,证明括号内的中缀表达式已经扫描完毕,要将栈顶运算符弹出,直到左括号(这时左括号出栈,但是不用加入到运算中);还要注意当运算符是左括号时,因为我把左括号定义为了最高优先级了,如果按正常的情况,当栈顶是左括号时,应当会马上输出但是这不符合我们的要求,希望的是紧接的运算符能无条件的入栈,所以在代码的实现过程中这是中特殊的情况用个if语句强调。
接下来是对中缀转换为后缀表达式实现的最主要的功能函数,就是运算符的出入栈的情况:

int oper_cp(const char&a, const char& b){    int c = op_pri(a);    int d = op_pri(b);    if (c == 0 && d == 3)        return -1;//当左括号碰到右括号时,定义为一个特殊状态,此状态是直接把栈中的左括号操作符弹出    //右括号也不进栈    else if (d == 3)//定义优先级时,以整数大小来对应优先级的大小。但是当输入为左括号时,需要无条件        //进栈,所以左括号的优先级要最高。碰到右括号时要无条件出栈直到那个栈顶的操作符是左括号。所以        //右括号的优先级要最低。        //但是在右括号进栈后,若紧接的操作符是+、-、*、/的话,需要进栈,这个条件语句就是在这种情况下        //对应的操作符直接进栈        return 1;    else if (c > d){        return 1;    }    else        return 0;}

有了上述两个函数的支持,接下来就是直接对中缀表达式求值的函数,这些问题只是逻辑问题,我的代码实现如下:

void exp(Stack<double> &val, Stack<char> &op){    char c{};    string s{};    do    {        c = getchar();        if ((c >= '0'&&c <= '9') || c == '.')        {            s.push_back(c);            continue;        }        else        {            if (s != "\0")            {                val.push(atof(s.c_str()));                s = "\0";            }        }        if (c == '+' || c == '-' || c == '*' || c == '/' || c == ')' || c == '(')        {            char b;            if (op.empty()){                op.push(c);                continue;            }            while (1)            {                if (!op.empty())                    b = op.peek();                else                {                    op.push(c);                    break;                }                int m = oper_cp(c, b);                if (m == 1)                {                    op.push(c);                    break;                }                if (m == 0)                {                    b = op.pop();                    double c, d;                    d = val.pop();                    c = val.pop();                    switch (b)                    {                    case '+':                        val.push(c + d);                        break;                    case '-':                        val.push(c - d);                        break;                    case'*':                        val.push(c*d);                        break;                    case'/':                        val.push(c / d);                        break;                    }                    continue;                }                if (m == -1)                {                    op.pop();                    break;                }            }        }    } while (c != '\n');    while (!op.empty()){        char b = op.pop();        double e, d;        d = val.pop();        e = val.pop();        switch (b)        {        case '+':            val.push(e + d);            break;        case '-':            val.push(e - d);            break;        case'*':            val.push(e*d);            break;        case'/':            val.push(e / d);            break;        }    }}

调用这些函数的main函数如下:

#include<iostream>#include<string>#include "Stack.h"using namespace std;void exp(Stack<double> &val, Stack<char> &op);int op_pri(const char& );int oper_cp(const char&a, const char& b);int  main(){    Stack<double> val;    Stack<char> op;    exp(val, op);    cout << val.pop();}int oper_cp(const char&a, const char& b){    int c = op_pri(a);    int d = op_pri(b);    if (c == 0 && d == 3)        return -1;//当左括号碰到右括号时,定义为一个特殊状态,此状态是直接把栈中的左括号操作符弹出    //右括号也不进栈    else if (d == 3)//定义优先级时,以整数大小来对应优先级的大小。但是当输入为左括号时,需要无条件        //进栈,所以左括号的优先级要最高。碰到右括号时要无条件出栈直到那个栈顶的操作符是左括号。所以        //右括号的优先级要最低。        //但是在右括号进栈后,若紧接的操作符是+、-、*、/的话,需要进栈,这个条件语句就是在这种情况下        //对应的操作符直接进栈        return 1;    else if (c > d){        return 1;    }    else        return 0;}int op_pri(const char& a){    switch (a){    case ')':        return 0;    case '+':case '-':        return 1;    case '*':case'/':        return 2;    case '(':        return 3;    }}void exp(Stack<double> &val, Stack<char> &op){    char c{};    string s{};    do    {        c = getchar();        if ((c >= '0'&&c <= '9') || c == '.')        {            s.push_back(c);            continue;        }        else        {            if (s != "\0")            {                val.push(atof(s.c_str()));                s = "\0";            }        }        if (c == '+' || c == '-' || c == '*' || c == '/' || c == ')' || c == '(')        {            char b;            if (op.empty()){                op.push(c);                continue;            }            while (1)            {                if (!op.empty())                    b = op.peek();                else                {                    op.push(c);                    break;                }                int m = oper_cp(c, b);                if (m == 1)                {                    op.push(c);                    break;                }                if (m == 0)                {                    b = op.pop();                    double c, d;                    d = val.pop();                    c = val.pop();                    switch (b)                    {                    case '+':                        val.push(c + d);                        break;                    case '-':                        val.push(c - d);                        break;                    case'*':                        val.push(c*d);                        break;                    case'/':                        val.push(c / d);                        break;                    }                    continue;                }                if (m == -1)                {                    op.pop();                    break;                }            }        }    } while (c != '\n');    while (!op.empty()){        char b = op.pop();        double e, d;        d = val.pop();        e = val.pop();        switch (b)        {        case '+':            val.push(e + d);            break;        case '-':            val.push(e - d);            break;        case'*':            val.push(e*d);            break;        case'/':            val.push(e / d);            break;        }    }}
0 0