利用栈结构对中缀表达式进行求值运算
最近看到邓公讲利用栈来求解中缀表达式的课程,讲的十分清楚,由于课程是用c++写的代码,我这里用Java简单实现下。
中缀表达式是一个通用的算术或逻辑公式表示方法。我们平时做的四则运算将数字与运算符拼接起来就是中缀表达式。算法思想比较清晰明了,下面我列下算法过程:
- 首先创建两个栈,一个是操作符栈,用于保存等待参与计算的运算符,一个是数值栈,用来保存待参与计算的数值;
- 确定各个运算符的优先级,以当前要计算的运算符,与操作符栈顶运算符为比较维度,这里用二维表格表示;
- 依次正序遍历原字符,根据字符类型有以下计算:
- 如果是数字,直接入数值栈;
- 如果是操作符,根据当前操作符优先级与操作栈栈顶操作符优先级比较结果,需要进行一下区分:
- 如果当前操作符优先级高于操作栈栈顶操作符,比如当前计算符为
*
,栈顶操作符为+
,那么当前操作符*
的计算需要被推迟,所以当前操作符入操作符栈; - 如果优先级相等,说明当前操作符为右括号
)
,此时把操作符栈顶左括号(
出栈; - 如果当前操作符优先级低于操作栈栈顶操作符,那么此时实施计算(数值栈依次弹出两个值,与当前运算符计算),将计算结果入数值栈,同时考虑到当前运算符可能比操作栈内部其他的操作符优先级低,所以需要继续计算栈内可计算的数据;
- 如果当前操作符优先级高于操作栈栈顶操作符,比如当前计算符为
- 遍历完一遍后,此时栈内可能还有计算剩余,此时从数值栈直接弹出数据进行计算,直至操作符栈为空,计算完毕。
具体细节还是挺多的,比较麻烦是需要进行向前探测是否还有运算符需要计算,这里直接用递归就可以了。实现代码如下:
/** * 四则运算,支持优先级 * <p> * 例如计算:1 + 2 * 3 - ( 1 + 2 / 3 * ( 2 + 3)) * * @author mingshan */ public class Calculator { /** * 支持的运算符 */ private static final String[] SUPPORT_OPERATOR = {"+", "-", "*", "/", "(", ")", "\0"}; /** * 操作符索引map */ private static final Map<String, Integer> OPERATOR_INDEX_MAP = new HashMap<>(); static { for (int i = 0; i < SUPPORT_OPERATOR.length; i++) { OPERATOR_INDEX_MAP.put(SUPPORT_OPERATOR[i], i); } } /** * 运算符优先级表 栈顶运算符索引,当前运算符索引 */ private static final String[][] PRIORITY_TABLE = { // 当前运算符 + - * / ( ) \0 // 栈顶的运算符 {">", ">", "<", "<", "<", ">", ">"}, // + {">", ">", "<", "<", "<", ">", ">"}, // - {">", ">", ">", ">", "<", ">", ">"}, // * {">", ">", ">", ">", "<", ">", ">"}, // / {"<", "<", "<", "<", "<", "=", ">"}, // ( {" ", " ", " ", " ", " ", " ", ">"}, // ) {"<", "<", "<", "<", "<", "<", "="}, // \0 }; public static void main(String[] args) { //solution("1 + 2 * 3 - ( 1 + 2 / 3 * ( 2 + 3))"); System.out.println(solution("1 + 2")); System.out.println(solution("1 * 2 + 3")); System.out.println(solution("1 + 2 * 3")); System.out.println(solution("1 + 2 * 3 - 4 / 2 + 6 / 2 - 7")); System.out.println(solution("1 + 2 * (3 * (1 + 2))")); } public static int solution(String expression) { if (expression == null || expression.length() == 0) { return 0; } // 操作栈 Stack<Character> operatorStack = new Stack<>(); // 数值栈 Stack<Integer> numberStack = new Stack<>(); char[] chars = expression.toCharArray(); for (int i = 0; i < chars.length; i++) { calculate(operatorStack, numberStack, String.valueOf(chars[i]), i == chars.length - 1); } while (!operatorStack.isEmpty()) { calculate(operatorStack, numberStack, String.valueOf(numberStack.pop()), true); } return numberStack.pop(); } /** * 根据传入的 {@code item} 元素,进行计算 * * @param operatorStack 操作数栈 * @param numberStack 数值栈 * @param item 传入的元素 * @param isLast 当前元素是否为实际数值栈中栈顶元素 */ private static void calculate(Stack<Character> operatorStack, Stack<Integer> numberStack, String item, boolean isLast) { if (" ".equals(item)) { return; } char[] items = item.toCharArray(); // 字符是数字 if (Character.isDigit(items[0])) { int itemInt = Integer.parseInt(item); // 判断是不是最后一位 if (isLast) { int result = calculate(itemInt, Integer.parseInt(String.valueOf(numberStack.pop())), operatorStack.pop()); numberStack.push(result); } else { numberStack.push(itemInt); } } else if (OPERATOR_INDEX_MAP.containsKey(item)) { handleOperator(operatorStack, numberStack, items[0]); } else { throw new IllegalArgumentException("不支持的运算符:" + item); } } /** * 处理操作符计算 * * @param operatorStack 操作栈 * @param numberStack 数值栈 * @param currOperator 当前操作符 */ private static void handleOperator(Stack<Character> operatorStack, Stack<Integer> numberStack, char currOperator) { if (operatorStack.isEmpty()) { operatorStack.push(currOperator); } else { // 字符是操作符 Character stackChar = operatorStack.peek(); Integer stackIndex = OPERATOR_INDEX_MAP.get(String.valueOf(stackChar)); Integer currIndex = OPERATOR_INDEX_MAP.get(String.valueOf(currOperator)); String order = PRIORITY_TABLE[stackIndex][currIndex]; switch (order) { // 当前操作符优先级高于操作栈栈顶操作符 case "<": // 计算推迟,当前操作符入栈 operatorStack.push(currOperator); break; case "=": // 优先级级相等,说明当前操作符为右括号,此时把括号去掉 operatorStack.pop(); break; case ">": // 当前操作符优先级低于操作栈栈顶操作符,此时实施计算 int num1 = numberStack.pop(); int num2 = numberStack.pop(); numberStack.push(calculate(num1, num2, operatorStack.pop())); // 继续计算栈内可计算的数据 handleOperator(operatorStack, numberStack, currOperator); break; default: throw new IllegalArgumentException("无效的比较结果:" + order); } } } /** * 计算结果 * * @param num1 操作数1 * @param num2 操作数2 * @param item 运算符 * @return 计算结果 */ private static int calculate(int num1, int num2, char item) { switch (String.valueOf(item)) { case "+": return num2 + num1; case "-": return num2 - num1; case "*": return num2 * num1; case "/": return num2 / num1; default: throw new UnsupportedOperationException("无效的的运算符:" + item); } } }
title: 利用栈结构对中缀表达式进行求值运算
tags: [算法,中缀表达式]
author: Mingshan
categories: [算法]
date: 2020-06-14
本文来自博客园,作者:mingshan,转载请注明原文链接:https://www.cnblogs.com/mingshan/p/17793509.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)