227. 基本计算器 II

找不到原题了,在leetcode找到类似的题目,不过leetcode的题目上没有括号的要求,代码实现中是有括号的。

题目:https://leetcode.cn/problems/basic-calculator-ii/

代码:

折叠复制代码
  1. public class Solution {
  2. private static final Character add = '+';
  3. private static final Character sub = '-';
  4. private static final Character mult = '*';
  5. private static final Character divi = '/';
  6. private static final Character parenthesesLeft = '(';
  7. private static final Character parenthesesRight = ')';
  8. /**
  9. * 符号map key为计算符号 value为优先级
  10. */
  11. private static final Map<Character, Integer> symbolMap = new HashMap<>();
  12. static {
  13. symbolMap.put(add, 0);
  14. symbolMap.put(sub, 0);
  15. symbolMap.put(mult, 1);
  16. symbolMap.put(divi, 1);
  17. symbolMap.put(parenthesesLeft, 2);
  18. symbolMap.put(parenthesesRight, 2);
  19. }
  20. public int calculate(String s) {
  21. List<Object> expressionList = this.parseExpression(s);
  22. System.out.println("解析后:" + expressionList);
  23. expressionList = this.toReversePolishNotation(expressionList);
  24. System.out.println("逆波兰式:" + expressionList);
  25. return this.calculate(expressionList).intValue();
  26. }
  27. /**
  28. * 解析表达式
  29. *
  30. * @param expression
  31. * @return
  32. */
  33. public List<Object> parseExpression(String expression) {
  34. List<Object> list = new ArrayList<>();
  35. StringBuilder numStr = new StringBuilder();
  36. //去空格
  37. expression = expression.replaceAll(" ", "");
  38. //遍历字符串,提取数字和计算符号
  39. for (Character c : expression.toCharArray()) {
  40. if (symbolMap.containsKey(c)) {
  41. //计算符号
  42. if (numStr.length() > 0) {
  43. //数字转Long类型
  44. list.add(Long.valueOf(numStr.toString()));
  45. numStr = new StringBuilder();
  46. }
  47. list.add(c);
  48. } else {
  49. //数字
  50. numStr.append(c);
  51. }
  52. }
  53. if (numStr.length() > 0) {
  54. //数字转Long类型
  55. list.add(Long.valueOf(numStr.toString()));
  56. }
  57. return list;
  58. }
  59. /**
  60. * 转成逆波兰式(后缀表达式)
  61. *
  62. * @param expressionList
  63. * @return
  64. */
  65. public List<Object> toReversePolishNotation(List<Object> expressionList) {
  66. List<Object> list = new ArrayList<>();
  67. Stack<Character> stack = new Stack<>();
  68. //遍历表达式并转换
  69. for (Object item : expressionList) {
  70. if (symbolMap.containsKey(item)) {
  71. //计算符号
  72. if (parenthesesLeft.equals(item)) {
  73. //左括号入栈
  74. stack.push((Character) item);
  75. } else if (parenthesesRight.equals(item)) {
  76. //右括号,出栈直到碰到左括号
  77. while (!stack.isEmpty() && !parenthesesLeft.equals(stack.peek())) {
  78. list.add(stack.pop());
  79. }
  80. //将左括号弹出丢弃
  81. if (!stack.isEmpty()) {
  82. stack.pop();
  83. }
  84. } else {
  85. //其他符号,当前符号的优先等级小于栈内的,将栈内的全部出栈
  86. while (!stack.isEmpty() && !parenthesesLeft.equals(stack.peek()) && symbolMap.get(item) <= symbolMap.get(stack.peek())) {
  87. list.add(stack.pop());
  88. }
  89. //将当前符号入栈
  90. stack.push((Character) item);
  91. }
  92. } else {
  93. //数字
  94. list.add(item);
  95. }
  96. }
  97. //清空栈
  98. while (!stack.isEmpty()) {
  99. list.add(stack.pop());
  100. }
  101. return list;
  102. }
  103. /**
  104. * 通过逆波兰式进行计算
  105. *
  106. * @param expressionList
  107. * @return
  108. */
  109. public Long calculate(List<Object> expressionList) {
  110. Stack<Long> stack = new Stack<>();
  111. for (Object item : expressionList) {
  112. if (symbolMap.containsKey(item)) {
  113. //计算符号
  114. Long num2 = stack.pop();
  115. Long num1 = stack.pop();
  116. if (add.equals(item)) {
  117. stack.push(num1 + num2);
  118. } else if (sub.equals(item)) {
  119. stack.push(num1 - num2);
  120. } else if (mult.equals(item)) {
  121. stack.push(num1 * num2);
  122. } else if (divi.equals(item)) {
  123. stack.push(num1 / num2);
  124. }
  125. } else {
  126. //数字
  127. stack.push((Long) item);
  128. }
  129. }
  130. return stack.pop();
  131. }
  132. }

思路:分三步走
1、先解析整个字符串,主要是拆出数字和计算符号,因为数字是Long类型,符号是String类型,简单操作就直接用一个List<Object>类型的list接数据。
实现上比较简单,提前将计算符号用一个Map存起来(Set也可以,不过后续有需要用到计算符号的优先级,所以用Map),使用StringBuilder拼接数字,如果遇到计算符号或者字符串都读完了,就把StringBuilder的值转成Long类型数字。

2、使用逆波兰式(后缀表达式)的方法来做计算,逆波兰式相关的资料见:https://baike.baidu.com/item/%E9%80%86%E6%B3%A2%E5%85%B0%E5%BC%8F/128437
这是整个题目实现上最复杂的部分,主要是用到了Stack栈,通过符号的一个优先级(括号由于比较特殊,优先级最高,所以直接if中特殊处理的),来判断出栈或者清空Stack的时机,具体请看代码注释。

3、通过逆波兰式进行计算。
这个比较简单,还是使用到Stack栈,如果是数字就入栈,碰到计算符号就弹出前2个值,也就是出栈。然后根据计算符号的规则(加减乘除)计算后将结果再次入栈。最终栈内有且只会有一个数字,也就是答案。


觉得内容还不错?打赏个钢镚鼓励鼓励!!👍