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

输入的算术表达式是中缀表达式,由于操作符的优先级不同,不能直接进行求解,而后缀表达式的计算不需要判别操作符的优先级,所以我实现算术表达式的思路是: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;
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值