Java版 逆波兰表达式(后缀表达式)


逆波兰表达式

又称后缀表达式,将运算符写在操作数之后,便于计算机计算

  • 将中缀表达式转换成后缀表达式的8个步骤:
  1. 初始化两个栈,运算符栈s1和存储中间结果的栈s2

  2. 从左至右扫描中缀表达式

  3. 遇到操作数时,将其压入s2

  4. 遇到运算符时,将其与s1栈顶运算符的优先级进行比较:

    1. 如果s1为空,或栈顶运算符为左括号"(",则直接将此运算符入栈
    2. 否则,若优先级比栈顶运算符的高,也将运算符压入s1
    3. 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4.1)与s1中的栈顶运算符相比较
  5. 遇到括号时:

    1. 如果是左括号"(",则直接压入s1
    2. 如果是右括号")",则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
  6. 重复步骤2至5,知道表达式最右边

  7. 将s1中剩余的运算符以此弹出并压入s2

  8. 依次弹出s2中的元素并输入,结果的逆序即为中缀表达式对应的后缀表达式。

  • 1+(2+3)*4 -5 的转换步骤

代码实现

/**
 * 转换成后缀表达式List
 *
 * @param ls 中缀表达式List
 * @return
 */
public static List<String> parseSuffixExpression(List<String> ls) {
    Stack<String> s1 = new Stack<>();//运算符栈
    Queue<String> s2 = new ArrayDeque<>();//临时队列

    for (String item : ls) {
        if (item.matches("\\d+")) {
            //3.如果是数字,直接入队s2
            s2.offer(item);
        } else if (isOper(item)) {
            //4.如果是运算符
            while (true) {
                if (s1.isEmpty() || "(".equals(s1.peek())) {
                    //4.1如果运算符栈s1是空的,或栈顶是左括号,则直接入栈
                    s1.push(item);
                    break;
                } else if (priority(item) > priority(s1.peek())) {
                    //4.2否则,若运算符优先级比栈顶的高,也入栈
                    s1.push(item);
                    break;
                } else {
                    //4.3 否则,直接把栈顶的元素入队,继续转到4.1与新的栈顶元素比较
                    s2.offer(s1.pop());
                }
            }
        } else if ("(".equals(item)) {
            //5.1如果遇到左括号( ,直接压入s1
            s1.push(item);
        } else if (")".equals(item)) {
            //5.2如果是右括号
            while (true) {
                String top = s1.pop();//弹出栈顶元素
                if ("(".equals(top)) {
                    //碰到左括号,丢弃这对括号
                    break;
                } else {
                    //否则入队
                    s2.offer(top);
                }
            }
        } else {
            throw new RuntimeException("无法识别的字符" + item + ",表达式不正确");
        }
    }
    while (!s1.isEmpty()) {
        //7.将s1剩余的表达式依次弹出并入队
        s2.offer(s1.pop());
    }

    List<String> res = new ArrayList<>();
    while (!s2.isEmpty()) {
        res.add(s2.poll());
    }
    return res;
}

后缀表达式的计算规则:从左到右遍历表达式的每个数字和符号,遇到是数字就进栈,遇到是符号,就将处于栈顶两个数字出栈,进行运算,运算结果进栈,一直到最终获得结果。

/**
 * 计算后缀表达式List,返回结果
 *
 * @param ls
 * @return
 */
public static int calculate(List<String> ls) {
    Stack<String> stack = new Stack<>();
    for (String item : ls) {
        if (item.matches("\\d+")) {
            //如果是数字则入栈
            stack.push(item);
        } else {
            //如果不是数字,则弹出栈顶和次栈顶元素进行计算
            int num2 = Integer.parseInt(stack.pop());
            int num1 = Integer.parseInt(stack.pop());
            int res = 0;
            if ("+".equals(item)) {
                res = num1 + num2;
            } else if ("-".equals(item)) {
                res = num1 - num2;
            } else if ("*".equals(item)) {
                res = num1 * num2;
            } else if ("/".equals(item)) {
                res = num1 / num2;
            } else {
                throw new RuntimeException("运算符有误");
            }
            //计算过的结果再次入栈
            stack.push("" + res);
        }
    }
    //栈中的结果就是表达是的结果,直接返回
    return Integer.parseInt(stack.pop());	
}

简单转换(括号法)

1.按照运算符的优先级对所有的运算单位加括号~

2.把运算符号移动到对应的括号后面

3.去除所有括号

例:

  1. 1+2
    • (1+2)
    • (1 2)+
    • 1 2 +
  2. 1+2×3
    • (1+(2 × 3))
    • (1+(2 3)×)
    • (1 (2 3 ×) ) +
    • 1 2 3 × +
  3. (2-1)×3+15÷(1+4)
    • (((2 - 1) × 3) + (15 ÷ (1+4)))
    • (((2 1) - × 3) + (15 ÷ (1 4)+))
    • (((2 1) - 3) × + (15 (1 4)+) ÷ )
    • (((2 1) - 3) × (15 (1 4)+) ÷ ) +
    • 2 1 - 3 × 15 1 4 + ÷ +
//完整java代码
import java.util.*;

/**
 * 逆波兰表达式(后缀表达式)
 * 支持整数、浮点 + - * / ( )运算符,可自动过滤非法字符
 */
public class PolandNotation {
    public static void main(String[] args) {
        String expression = "(2.1-1)* 3.1+  15/(2+4)";
        System.out.println("中缀表达式:" + expression);
        List<String> list = toInfixExpressionList(expression);
        System.out.println("中缀表达式List:" + list);
        List<String> suffixExpression = parseSuffixExpression(list);
        System.out.println("后缀表达式List:" + suffixExpression);
        float calculate = calculate(suffixExpression);
        System.out.println("计算结果:" + calculate);
    }

    /**
     * 转换成后缀表达式List
     *
     * @param ls 中缀表达式List
     * @return
     */
    public static List<String> parseSuffixExpression(List<String> ls) {
        Stack<String> s1 = new Stack<>();//运算符栈
        Queue<String> s2 = new ArrayDeque<>();//临时队列

        for (String item : ls) {
            if (item.matches(REGEX_FLOAT)) {
                //3.如果是数字,直接入队s2
                s2.offer(item);
            } else if (isOper(item)) {
                //4.如果是运算符
                while (true) {
                    if (s1.isEmpty() || "(".equals(s1.peek())) {
                        //4.1如果运算符栈s1是空的,或栈顶是左括号,则直接入栈
                        s1.push(item);
                        break;
                    } else if (priority(item) > priority(s1.peek())) {
                        //4.2否则,若运算符优先级比栈顶的高,也入栈
                        s1.push(item);
                        break;
                    } else {
                        //4.3 否则,直接把栈顶的元素入队,继续转到4.1与新的栈顶元素比较
                        s2.offer(s1.pop());
                    }
                }
            } else if ("(".equals(item)) {
                //5.1如果遇到左括号( ,直接压入s1
                s1.push(item);
            } else if (")".equals(item)) {
                //5.2如果是右括号
                while (true) {
                    String top = s1.pop();//弹出栈顶元素
                    if ("(".equals(top)) {
                        //碰到左括号,丢弃这对括号
                        break;
                    } else {
                        //否则入队
                        s2.offer(top);
                    }
                }
            } else {
                throw new RuntimeException("无法识别的字符" + item + ",表达式不正确");
            }
        }
        while (!s1.isEmpty()) {
            //7.将s1剩余的表达式依次弹出并入队
            s2.offer(s1.pop());
        }

        List<String> res = new ArrayList<>();
        while (!s2.isEmpty()) {
            res.add(s2.poll());
        }
        return res;
    }

    /**
     * 是否操作符
     *
     * @param val 元素
     * @return
     */
    public static boolean isOper(String val) {
        return "+".equals(val) || "-".equals(val) || "*".equals(val) || "/".equals(val);
    }

    /**
     * 将中缀表达式转换成对应的List
     *
     * @param s 中缀表达式字符串
     * @return
     */
    public static List<String> toInfixExpressionList(String s) {
        List<String> ls = new ArrayList<>();
        int i = 0;//指针,用于遍历中缀表达式字符串
        String str;//对多位数的拼接工作
        char c;//每遍历到一个字符,放入c
        do {
            c = s.charAt(i);//获取元素
            if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')') {
                //如果元素是运算符直接添加到List
                ls.add(c + "");
                i++;
            } else if (Character.isDigit(c) || c == '.') {
                //如果元素是数字和小数点
                str = "";
                while (i < s.length() && (Character.isDigit(s.charAt(i)) || s.charAt(i) == '.')) {//直至下个不是小数点或数字的位置
                    str += "" + s.charAt(i++);
                }
                ls.add(str);
            } else {
                i++;
            }
        } while (i < s.length());

        return ls;
    }


    /**
     * 返回操作符优先级
     *
     * @param operString 操作符
     * @return
     */
    public static int priority(String operString) {
        if ("*".equals(operString) || "/".equals(operString)) {
            return 1;
        } else if ("+".equals(operString) || "-".equals(operString)) {
            return 0;
        } else {
            return -1;
        }
    }

    /**
     * 数字正则
     */
    private final static String REGEX_FLOAT = "\\d+||\\d*\\.\\d+||\\d*\\.?\\d+?e[+-]\\d*\\.?\\d+?||e[+-]\\d*\\.?\\d+?";

    /**
     * 计算后缀表达式List,返回结果
     *
     * @param ls
     * @return
     */
    public static float calculate(List<String> ls) {
        Stack<String> stack = new Stack<>();
        for (String item : ls) {
            if (item.matches(REGEX_FLOAT)) {
                stack.push(item);//如果是数字则入栈
            } else {
                //如果不是数字,则弹出栈顶和次栈顶元素进行计算
                Float num2 = Float.parseFloat(stack.pop());
                Float num1 = Float.parseFloat(stack.pop());
                Float res = 0f;
                if ("+".equals(item)) {
                    res = num1 + num2;
                } else if ("-".equals(item)) {
                    res = num1 - num2;
                } else if ("*".equals(item)) {
                    res = num1 * num2;
                } else if ("/".equals(item)) {
                    res = num1 / num2;
                } else {
                    throw new RuntimeException("运算符有误");
                }
                //计算过的结果再次入栈
                stack.push("" + res);
            }
        }
        //栈中的结果就是表达是的结果,直接返回
        return Float.parseFloat(stack.pop());
    }

}


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM