蓝桥杯 算法训练 表达式计算

版权声明:原创文章,未经许可不能转载 https://blog.csdn.net/qq_38499625/article/details/79690054

问题描述

  输入一个只包含加减乖除和括号的合法表达式,求表达式的值。其中除表示整除。
输入格式
  输入一行,包含一个表达式。
输出格式
  输出这个表达式的值。
样例输入
1-2+3*(4-5)
样例输出
-4
数据规模和约定
  表达式长度不超过100,表达式运算合法且运算过程都在int内进行。


问题分析:

算数表达式是由操作数、算术运算符和分隔符所组成的式子。为了方便,下面的讨论将仅限于含有二元运算符且操作数是一位整数的算术表达式的运算。

表达式一般有中缀表达式、后缀表达式和前缀表达式共三种表达形式,其中中缀表达式是将运算符放在两个操作数中间,这正是人们平时书写算术表达式的一种描述形式;后缀表达式(也称逆波兰表达式)是将运算符放在两个操作数之后,而前缀表达式是将运算符放在两个操作数之前。例如:中缀表达式A+(B-C/D)*E,对应的后缀表达式为ABD/-E*+,对应的前缀表达式为+A*-B/CDE。

由于运算符有优先级,所以在计算机内部使用中缀表达式描述时,对计算机是非常不方便的,特别是带括号的更麻烦。而后缀表达式中既无运算符优先级又无括号的约束问题,因为在后缀表达式中运算符出现的顺序正是计算的顺序,所以计算一个后缀表达式的值要比计算一个中缀表达式的值简单得多。由此,求计算表达式的值可以分为两步来进行,第一步先将原算术表达式转换成后缀表达式,第二步在对后缀表达式求值。下面分别给出这两个步骤的实现方法。

1.将原算术表达式转换成后缀表达式

由于原算术表达式与后缀表达式中的操作数所出现的先后次序是完全一样的,只是运算符出现的先后次序不一样,所以转换的重点放在运算符的处理上。首先设定运算符的优先级:

运算符的优先级
运算符 ((左括号) +(加)、-(减) *(乘)、/(除)、%(取模) ^(幂)
优先级 0 1 2 3

表中的优先级由高到低分别从3~0的数字来表示,数字越大,表示其运算符的优先级越高。要使运算符出现的次序与真正的算术运算顺序一致,就要使优先级数高的以及括号内的运算符出现在前,所以把在算术运算符转换成后缀表达式时,要使用一个栈来保留还未送往后缀表达式的运算符,此栈成为运算符栈。实现算法的基本思想如下:

(1)初始化一个运算符栈

(2)从算术表达式输入的字符串中从左往右读取一个字符

(3)若当前字符是操作数,则直接送往后缀表达式。

(4)若当前字符是左括号,将其压进运算符栈。

(5)若当前字符为运算符时,则:

a.当运算符栈为空,则将其压入运算符栈

b.当此运算符的优先级高于栈顶运算符,则将此运算符压入运算符栈;否则弹出栈顶运算符送往后缀式,并将当前运算符压栈,重复步骤(5)。

(6)若当前字符是右括号时,反复将栈顶元素弹出,并送往后缀表达式,直到栈顶符号是左括号为止,再将右括号出栈丢弃。

(7)若读取还未完毕,则跳转到(2)。

(8)若读取完毕,则将栈中剩余的所有运算符弹出并送往后缀表达式。

2.计算后缀表达式的值

要计算后缀表达式的值比较简单,只要先找到运算符,再去找前面最后出现的两个操作数,从而构成一个最小的算术表达式进行运算,在计算过程中也需利用一个栈来保留后缀表达式中还未参与运算的操作数,此栈也称为操作数栈。计算后缀表达式值的算法的基本思想如下:

(1)初始化一个操作数栈。

(2)从左至右一次读入后缀表达式中的字符:

a.若当前字符是操作数,则压入操作数栈。

b.若当前字符是运算符,则从栈顶弹出两个操作数并分别作为第二个操作数和第一个操作数参与运算,再将运算结果压入操作数栈。

(3)重复步骤(2)直到读入的后缀表达式结束为止,则操作数栈中的栈顶元素即为后缀表达式的计算结果。

按照以上的算法,可以在计算机内首先完成表达式的转换,然后计算表达式的值,这样计算机在计算时相对比较节省系统资源。


实现代码如下:

package num8;

import java.util.Scanner;
import java.util.Stack;

public class Demo4 {

    public String convertToPostfix(String expression) throws Exception {
        Stack st=new Stack();// 用于存放函数运行过程中的括号和运算符(函数结束时此栈为空)
        String postfix = new String();// 用于输出的后缀表达式
        for (int i = 0; expression != null && i < expression.length(); i++) {
            char c = expression.charAt(i);// 指定索引处的 char 值
            if (' ' != c) {// 字符c不为空格
                if (isOpenParenthesis(c)) {
                    st.push(c);// 为开括号,压栈
                } else if (isCloseParenthesis(c)) {// 为闭括号,栈中运算符逐个出栈并放入用于输出的栈,直到遇到开括号。开括号出栈并丢弃
                    Character ac = (Character) st.pop();// 移除栈顶元素
                    while (!isOpenParenthesis(ac.charValue())) {// 一直到为开括号为止
                        postfix = postfix.concat(ac.toString());// 串联到后缀表达式的结尾
                        ac = (Character) st.pop();
                    }
                } else if (isOperator(c)) {// 为运算符
                    if (!st.empty()) {// 栈非空,取出栈中优先级高的操作符串联到后缀表达式的结尾
                        Character ac = (Character) st.pop();
                        while (ac != null
                                && priority(ac.charValue()) >= priority(c)) {// 优先级比较
                            postfix = postfix.concat(ac.toString());// 串联到后缀表达式的结尾

// **************************************解决方案一************************
//                            if(st.empty()) {
//                                break;
//                            }
//                            ac = (Character) st.pop();
// **************************************解决方案一************************

// *************************************解决方案二*************************
//                         动用解决方案二,把解决方案一的代码注释,下面的也要注释
                            try {
                                ac = (Character) st.pop();
                            }catch (Exception e){
                                ac = null;
                            }
// *************************************解决方案二*************************
                        }
//                        解决方案a
//                        if (ac != null&&(priority(ac.charValue()) < priority(c))) {// 如果最后一次取出的优先级低的操作符,重新压栈
//                            st.push(ac);
//                        }

//                        解决方案b
                        if (ac != null) {// 如果最后一次取出的优先级低的操作符,重新压栈
                            st.push(ac);
                        }
                    }
                    st.push(c);
                } else {// 为操作数,串联到后缀表达式的结尾
                    postfix = postfix.concat(String.valueOf(c));
                }
            }
        }

        while (!st.empty()) {// 栈中剩余的所有操作符串联到后缀表达式的结尾
            postfix = postfix.concat(String.valueOf(st.pop()));// 串联到后缀表达式的结尾
        }

        return postfix;
    }

    public int numberCalculate(String postfix) throws Exception {
        Stack st=new Stack();
        for (int i = 0; postfix != null && i < postfix.length(); i++) {
            char c = postfix.charAt(i);// 指定索引处的 char 值
            if (isOperator(c)) {		// 当为操作符时
                // 取出两个操作数
                int d2 = Integer.valueOf(st.pop().toString());
                int d1 = Integer.valueOf(st.pop().toString());
                int d3 = 0;
                if ('+' == c) {		  // 加法运算
                    d3 = d1 + d2;
                } else if ('-' == c) {// 减法运算
                    d3 = d1 - d2;
                } else if ('*' == c) {// 乘法运算
                    d3 = d1 * d2;
                } else if ('/' == c) {// 除法运算
                    d3 = d1 / d2;
                } else if ('^' == c) {// 幂运算
                    d3 = (int) Math.pow(d1, d2);
                } else if ('%' == c) {
                    d3 = d1 % d2;
                }
                st.push(d3);
            } else {// 当为操作数时
                st.push(c);
            }
        }
        return (int) st.pop();// 返回运算结果
    }

    // 判断字符串是否为运算符
    public boolean isOperator(char c) {
        if ('+' == c || '-' == c || '*' == c || '/' == c || '^' == c
                || '%' == c) {
            return true;
        } else {
            return false;
        }
    }

    // 判断字符串是否为开括号
    public boolean isOpenParenthesis(char c) {
        return '(' == c;
    }

    // 判断字符串是否为闭括号
    public boolean isCloseParenthesis(char c) {
        return ')' == c;
    }

    // 判断运算法的优先级
    public int priority(char c) {
        if (c == '^') {// 为幂运算
            return 3;
        }
        if (c == '*' || c == '/' || c == '%') {// 为乘、除、取模运算
            return 2;
        } else if (c == '+' || c == '-') {// 为加、减运算
            return 1;
        } else { // 其他
            return 0;
        }
    }

    public static void main(String[] args) throws Exception {
        Demo4 p = new Demo4();
		Scanner sc=new Scanner(System.in);
		String str=sc.nextLine();
        String postfix = p.convertToPostfix(str);// 转化为后缀表达式
        System.out.println(p.numberCalculate(postfix));// 对后缀表达式求值后,并输出
    }
}
代码如上,欢迎指点!














猜你喜欢

转载自blog.csdn.net/qq_38499625/article/details/79690054