zoukankan      html  css  js  c++  java
  • [编程题] 四则运算

    [编程题] 四则运算

    题目描述

    请实现如下接口

    /* 功能:四则运算
    * 输入:strExpression:字符串格式的算术表达式,如: "3+2*{1+2*[-4/(8-6)+7]}"
    * 返回:算术表达式的计算结果
    */
    
    public static int calculate(String strExpression)
    {
        /* 请实现*/
        return 0;
    } 
    

    约束:

    1. pucExpression字符串中的有效字符包括[‘0’-‘9’],‘+’,‘-’, ‘*’,‘/’ ,‘(’, ‘)’,‘[’, ‘]’,‘{’ ,‘}’。
    2. pucExpression算术表达式的有效性由调用者保证;

    输入描述:

    输入一个算术表达式
    

    输出描述:

    得到计算结果
    

    输入例子1:

    3+2*{1+2*[-4/(8-6)+7]}
    

    输出例子1:

    25
    

    个人方法(python)

    # !/usr/bin/env python2
    # -*- coding:utf-8 -*-
    
    opts = '+-*/()[]{}'
    Wopt = {'(':0,'[':1,'{':2,'+':3,'-':3, '*':4, '/':4}
    
    # 字符串 转 中缀表达式(infix)  : str->list
    # 目的为了 分离 多位的整数、负数以及运算符
    def str2infix(line):
        infix = []
        numtmp = ''
        for ch in line:
            if ch not in opts:
                numtmp += ch
            else:
                if ch == '-':  # 分离负数
                    if len(infix) != 0:
                        if infix[-1] in "([{" and numtmp=='':
                            numtmp += ch
                            continue
                if numtmp != '':
                    infix.append(numtmp)
                    numtmp = ''
                infix.append(ch)
    
        if numtmp != '':
            infix.append(numtmp)
    
        return infix
    # 中缀表达式(infix) 转 后缀表达式(posfix)
    def infix2posfix(infix):
        posfix = []
        oprtmp = []
        for v in infix:
            if v not in opts:
                posfix.append(v)
            else:
                if len(oprtmp) == 0:
                    oprtmp.append(v)
                elif v in '([{':
                    oprtmp.append(v)
                elif v in ')]}':
                    while True:
                        ele = oprtmp.pop()
                        if ele in '([{':
                            break
                        # posfix.append(ele)
                        # if Wopt[ele] < Wopt[oprtmp[-1]]:
                        posfix.append(ele)
                            # break
    
                elif Wopt[v] <= Wopt[oprtmp[-1]]:
                    while Wopt[v] <= Wopt[oprtmp[-1]]:
                        posfix.append(oprtmp.pop())
                        if len(oprtmp) == 0:
                            break
                    oprtmp.append(v)
                else:
                    oprtmp.append(v)
    
        while len(oprtmp) != 0:
            posfix.append(oprtmp.pop())
        return posfix
    # 后缀表达式求值
    def calculate(posfix):
        expr = []
        for ele in posfix:
            if ele in '-+/*':
                n1, n2 = expr.pop(), expr.pop()
                if ele == '+':
                    expr.append(n2 + n1)
                elif ele == '-':
                    expr.append(n2 - n1)
                elif ele == '*':
                    expr.append(n2 * n1)
                else:
                    expr.append(n2 / n1)
            else:
                expr.append(int(ele))
        return expr
    
    # 字符串中的有效字符包括[‘0’-‘9’],‘+’,‘-’, ‘*’,‘/’ ,‘(’, ‘)’,‘[’, ‘]’,‘{’ ,‘}’。
    try:
        while True:
            line = raw_input()
            # line = '3+2*{1+2*[-4/(8-6)+7]}'
            # line = '3*5+8-0*3-6+0+0'
            # line = '5-3+9*6*(6-10-2)'
            # line = '3-10+(0+(10+5+3)-10)'
            if line=='':
                break
    
            infix = str2infix(line)
            posfix = infix2posfix(infix)
            # print infix
            # print posfix
            print calculate(posfix)[0]
            # break
    except:
        pass
    

    优秀解析

    1. Python一行代码(py2,py3)

    # python2
    print(int(eval(raw_input().replace("{","(").replace("}",")").replace("[","(").replace("]",")"))))
    # python3
    print(int(eval(input().replace("{","(").replace("}",")").replace("[","(").replace("]",")"))))
    

    说明:
    eval字符串参数合法的字符包括”+, -, *, /, (, )”,”0-9”(没有“[]”,”{}“)

    所以下面的仅仅是处理带小括号的 表达式求值。同时上面也仅仅是把大、中括号替换为小括号

    # python2
    print(eval(raw_input()))
    # python3
    print(eval(input()))
    

    2. 传统方法,计算中缀表达式(java)

    传统方法,直接通过两个栈,计算中缀表达式的值

    import java.util.*;
    public class Main{
        // 用于存放一个正括号的集合, 用于简化代码
        static Set<Character> brace = new HashSet<>();
        public static void main(String ... args){
            Scanner sc = new Scanner(System.in);
            // 初始化正括号集合
            brace.add('{');
            brace.add('(');
            brace.add('[');
            while(sc.hasNextLine()){
                // 对字符串做初始化处理,原则有二:
                // 1、处理负数,这里在-前面的位置加入一个0,如-4变为0-4,
                // 细节:注意-开头的地方前面一定不能是数字或者反括号,如9-0,(3-4)-5,这里地方是不能加0的
                // 它的后面可以是数字或者正括号,如-9=>0-9, -(3*3)=>0-(3*3)
                // 2、处理字符串,在最后的位置加#, 主要是为了防止最后一个整数无法处理的问题
                String exp = sc.nextLine().replaceAll("(?<![0-9)}\]])(?=-[0-9({\[])", "0") + "#";
                System.out.println(calculate(exp));
            }
        }
        private static int calculate(String exp){
            // 初始化栈
            Stack<Integer> opStack = new Stack<>();
            Stack<Character> otStack = new Stack<>();
             
            // 整数记录器
            String num = "";
            for(int i = 0; i < exp.length(); i++){
                // 抽取字符
                char c = exp.charAt(i);
                // 如果字符是数字,则加这个数字累加到num后面
                if(Character.isDigit(c)){
                    num += c;
                }
                // 如果不是数字
                else{
                    // 如果有字符串被记录,则操作数入栈,并清空
                    if(!num.isEmpty()){
                        int n = Integer.parseInt(num);
                        num = "";
                        opStack.push(n);
                    }
                    // 如果遇上了终结符则退出
                    if(c == '#')
                        break;
                    // 如果遇上了+-
                    else if(c == '+' || c == '-'){
                        // 空栈或者操作符栈顶遇到正括号,则入栈
                        if(otStack.isEmpty() || brace.contains(otStack.peek())){
                            otStack.push(c);
                        } else {
                            // 否则一直做弹栈计算,直到空或者遇到正括号为止,最后入栈
                            while(!otStack.isEmpty() && !brace.contains(otStack.peek()))
                                popAndCal(opStack, otStack);
                            otStack.push(c);
                        }
                    }
                    // 如果遇上*/
                    else if(c == '*' || c == '/'){
                        // 空栈或者遇到操作符栈顶是括号,或者遇到优先级低的运算符,则入栈
                        if(otStack.isEmpty()
                                || brace.contains(otStack.peek())
                                || otStack.peek() == '+' || otStack.peek() == '-'){
                            otStack.push(c);
                        }else{
                            // 否则遇到*或/则一直做弹栈计算,直到栈顶是优先级比自己低的符号,最后入栈
                            while(!otStack.isEmpty()
                                    && otStack.peek() != '+' && otStack.peek() != '-'
                                    && !brace.contains(otStack.peek()))
                                popAndCal(opStack, otStack);
                            otStack.push(c);
                        }
                    } else {
                        // 如果是正括号就压栈
                        if(brace.contains(c))
                            otStack.push(c);
                        else{
                            // 反括号就一直做弹栈计算,直到遇到正括号为止
                            char r = getBrace(c);
                            while(otStack.peek() != r){
                                popAndCal(opStack, otStack);
                            }
                            // 最后弹出正括号
                            otStack.pop();
                        }
                    }
                }
            }
            // 将剩下的计算完,直到运算符栈为空
            while(!otStack.isEmpty())
                popAndCal(opStack, otStack);
            // 返回结果
            return opStack.pop();
        }
        private static void popAndCal(Stack<Integer> opStack, Stack<Character> otStack){
            int op2 = opStack.pop();
            int op1 = opStack.pop();
            char ot = otStack.pop();
            int res = 0;
            switch(ot){
                case '+':
                    res = op1 + op2;
                    break;
                case '-':
                    res = op1 - op2;
                    break;
                case '*':
                    res = op1 * op2;
                    break;
                case '/':
                    res = op1 / op2;
                    break;
            }
            opStack.push(res);
        }
        private static char getBrace(char brace){
            switch(brace){
                case ')':
                    return '(';
                case ']':
                    return '[';
                case '}':
                    return '{';
            }
            return '#';
        }
    }
    

    3. 中缀字符串转变为后缀字符串数组并求解(c++)

    //思路:
    //1.字符串预处理,针对可能出现的“{,},[,],-”等特殊情况进行替换,判断‘-’是负号还是减号,负号前面+0,转变成减法运算
    //2.将中缀字符串转变为后缀字符串数组
    //3.对后缀字符串数组进行求解
    #include<iostream>
    #include<vector>
    #include<string>
    #include<stack>
    #include<sstream>
    using namespace std;
    bool cmpPriority(char top,char cur)//比较当前字符与栈顶字符的优先级,若栈顶高,返回true
    {
        if((top=='+' || top=='-') && (cur=='+' || cur=='-'))
            return true;
     if((top=='*' || top=='/') && (cur=='+' || cur=='-'|| top=='*' || top=='/'))
            return true;
        if(cur==')')
            return true;
        return false;
    }
    void preProcess(string &str)//对字符串进行预处理
    {
        for(int i=0;i<str.size();++i)
        {
            if(str[i]=='{')//将‘{、}、[,]’替换成'()'
                str[i]='(';
            else if(str[i]=='}')
                str[i]=')';
            else if(str[i]=='[')
                str[i]='(';
            else if(str[i]==']')
                str[i]=')';
            else if(str[i]=='-')
            {
                if(i==0)//将'-'前面添加0转变成减法运算
                    str.insert(0,1,'0');
                else if(str[i-1]=='(')
                    str.insert(i,1,'0');
      }
     }
    }
    vector<string> mid2post(string &str)
    {
        vector<string>vstr;
        stack<char>cstack;
        for(int i=0;i<str.size();++i)//扫描字符串
        {
            string temp="";
            if(str[i]>='0' && str[i]<='9')//若是数字
            {
                temp+=str[i];
                while(i+1<str.size() && str[i+1]>='0' && str[i+1]<='9')
                {
                    temp+=str[i+1];//若是连续数字
                    ++i;
       }
                vstr.push_back(temp);
      }
            else if(cstack.empty() || str[i]=='(')//若栈空或者字符为'('
                cstack.push(str[i]);
            else if(cmpPriority(cstack.top(),str[i]))//若栈顶元素优先级较高,栈顶元素出栈
            {
                if(str[i]==')')//若当前字符是右括号,栈中元素出栈,入字符串数组中,直到遇到'('
                {
                    while(!cstack.empty() && cstack.top()!='(')
                    {
                        temp+=cstack.top();
                        cstack.pop();
                        vstr.push_back(temp);
                        temp="";
                    }
                    cstack.pop();                   
                }
                else//栈中优先级高的元素出栈,入字符串数组,直到优先级低于当前字符
                {
                    while(!cstack.empty() && cmpPriority(cstack.top(),str[i]))
                    {
                        temp+=cstack.top();
                        cstack.pop();
                        vstr.push_back(temp);
                        temp="";
                    }
                    cstack.push(str[i]);
       }
            }
            else//当前字符优先级高于栈顶元素,直接入栈
             cstack.push(str[i]);
        }
        while(!cstack.empty())//栈中还存在运算符时,出栈,存入字符串数组
        {
            string temp="";
            temp+=cstack.top();
            cstack.pop();
            vstr.push_back(temp);
        }
        return vstr;
    }
    int calcPostExp(vector<string> & vstr)//对后缀表达式进行求值,主要是根据运算符取出两个操作数进行运算
    {
        int num,op1,op2;
        stack<int>opstack;
        for(int i=0;i<vstr.size();++i)
        {
            string temp=vstr[i];
            if(temp[0]>='0' && temp[0]<='9')//如果当前字符串是数字,利用字符串流转化为int型
            {
                stringstream ss;
                ss<<temp;
                ss>>num;
                opstack.push(num);
            }
            else if(vstr[i]=="+")//若是操作符,取出两个操作数,进行运算,并将结果存入
            {
                op2=opstack.top();
                opstack.pop();
                op1=opstack.top();
                opstack.pop();
                opstack.push(op1+op2);
            }
            else if(vstr[i]=="-")
            {
                op2=opstack.top();
                opstack.pop();
                op1=opstack.top();
                opstack.pop();
                opstack.push(op1-op2);
            }
            else if(vstr[i]=="*")
            {
                op2=opstack.top();
                opstack.pop();
                op1=opstack.top();
                opstack.pop();
                opstack.push(op1*op2);
            }
            else if(vstr[i]=="/")
            {
                op2=opstack.top();
                opstack.pop();
                op1=opstack.top();
                opstack.pop();
                opstack.push(op1/op2);
            }
        }
        return opstack.top();//最终的栈顶元素就是求解的结果
    }
    void calcExp(string str)
    {
        vector<string>vstr;
        preProcess(str);//对字符串进行预处理
        vstr=mid2post(str);//将中缀表达式转为后缀,保存在字符串数组中,方便下一步求解
        int res=calcPostExp(vstr);
        cout<<res<<endl;
    }
    int main()
    {
        string str;
        while(getline(cin,str))
        {
            calcExp(str);
     }
        return 0;
    }
    

    3. 统一小括号,中缀表达式转后缀并求解(c++)

    思路是很正常的思路

    1. 先把中括号和大括号换成小括号,方便后续处理

    2. 四则运算:中缀表达式转后缀表达式,中途计算算出后缀表达式结果

    #include<iostream>
    #include<string>
    #include<stack>//栈头文件
    using namespace std;
    string change_bracket(string exp);//将大括号和中括号转成小括号,同时,将负数x转成0-x的形式
    int mid_to_post(string exp);
    int calculate(int a, int b, char sym);
    int main()
    {
        string exp;
        while (cin >> exp)
        {
            exp = change_bracket(exp);
            int exp_post = mid_to_post(exp);
            cout << exp_post << endl;
        }
        return 0;
    }
    //把大括号和中括号换成小括号,以便减少后期过多的判断
    string change_bracket(string exp)
    {
        for (int i = 0; i < exp.size(); i++)
        {
            if (exp[i] == '{' || exp[i] == '[')
                exp[i] = '(';
            if (exp[i] == '}' || exp[i] == ']')
                exp[i] = ')';
        }
         
        //cout << exp;
        return exp;
    }
    int mid_to_post(string exp)
    {
        int flag = 0;//正负号标志,0为无正负号,1为正号,2为负号
        stack<int> exp_post;//数字栈
        stack<char> symbol;//符号栈
        for (int i = 0; i < exp.size(); i++)
        {
            char temp;
            if (isdigit(exp[i]))//为数字时
            {
                int j = i,num=0;
                while (i + 1 < exp.length() && isdigit(exp[i + 1])) i++;
                string str_num = exp.substr(j, i - j+1);
                for (int k = 0; k < str_num.size(); k++)
                    num = num * 10 + str_num[k] - '0';
                if (flag == 2)
                    num = 0 - num;
                flag = 0;
                exp_post.push(num);
            }  
            else if (exp[i] == '*' || exp[i] == '/' || exp[i] == '(')//为乘除时
                symbol.push(exp[i]);
            else if (exp[i] == '+'||exp[i] == '-')//为加减时
            {
                /*处理负号先*/
                if (!i || exp[i - 1]=='(')
                    if (exp[i] == '+')
                        flag = 1;
                    else
                        flag = 2;
     
                /*处理负号先_end*/
                while (!flag&&!symbol.empty() && symbol.top() != '(')//堆栈非空时,符号栈弹出符号,并结合数字栈计算
                {
                    int b = 0, a = 0;
                    char sym_temp;
                    b = exp_post.top();
                    exp_post.pop();
                    a = exp_post.top();
                    exp_post.pop();
                    sym_temp = symbol.top();
                    symbol.pop();
                    exp_post.push(calculate(a, b, sym_temp));//计算结果入栈
                }
                if(!flag) symbol.push(exp[i]);
            }
            else if (exp[i] == ')')//为右括号时
            {
                while (symbol.top() != '(')
                {
                    int b = 0, a = 0;
                    char sym_temp;
                    b = exp_post.top();
                    exp_post.pop();
                    a = exp_post.top();
                    exp_post.pop();
                    sym_temp = symbol.top();
                    symbol.pop();
                    exp_post.push(calculate(a, b, sym_temp));//计算结果入栈
                }
                symbol.pop();
            }
            else
                cout << "Input error!!!" << endl;
        }
        //循环结束后把剩下的符号弹出,并结合数字栈计算
        while (!symbol.empty())
        {
            int b = 0, a = 0;
            char sym_temp;
            b = exp_post.top();
            exp_post.pop();
            a = exp_post.top();
            exp_post.pop();
            sym_temp = symbol.top();
            symbol.pop();
            exp_post.push(calculate(a, b, sym_temp));//计算结果入栈
        }
        return exp_post.top();
    }
     
    int calculate(int a,int b,char sym)
    {
        switch (sym)
        {
            case '+': return a + b;
            case '-': return a - b;
            case '*': return a * b;
            case '/': return a / b;
        default:
            return 0;
            break;
        }
    }
    

    参考

    1. 简单算术表达式求值 https://blog.csdn.net/dnxbjyj/article/details/71248637

    2. 首页 > 试题广场 > 四则运算 https://www.nowcoder.com/questionTerminal/9999764a61484d819056f807d2a91f1e

  • 相关阅读:
    upc组队赛3 T-net【贪心】
    upc组队赛5 Assembly Required【思维】
    upc组队赛5 Bulbs
    upc组队赛5 Ingenious Lottery Tickets【排序】
    upc组队赛5 Hunter’s Apprentice 【判断多边形边界曲线顺逆时针】
    upc组队赛5 Ground Defense【枚举】
    upc组队赛4 Go Latin
    upc组队赛4 TV Show Game 【2-SAT】
    POJ 3250 Bad Hair Day【单调栈入门】
    016.NET5_MVC_视图组件扩展定制
  • 原文地址:https://www.cnblogs.com/oucbl/p/12556192.html
Copyright © 2011-2022 走看看