经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 其他 » 计算机原理 » 查看文章
计算器思想-中缀表达式转化为后缀表达式
来源:cnblogs  作者:pretty_spider  时间:2023/9/15 13:31:21  对本文有异议

计算机思维和人的思维的不同

对于一个算式3+2*(4-3)/5
人的思维是根据括号和符号优先级,优先计算括号中的数据,在进行乘法和除法,在处理加法运算
但是计算机的思维是线性的,计算机会按照算式的前后顺序,从前往后进行运算,这样会导致运算结果错误

计算机如何套用人的运算思维

想要让计算机具有人的”思维“,就需要使用栈,将数据和运算符号之间的顺序按照计算机可以理解的方式排列
想要改变规则,就需要将人理解的中缀表达式转换为后缀表达式
转化的规则是:

  • 中缀表达式转化成后缀表达式
    1.遇到操作数直接放入到集合中
    2.遇到操作符
    2.1当栈为空,或栈顶元素为(,直接放入到栈中
    2.2当优先级比栈顶元素高时,直接进栈
    2.3当优先级小于等于栈顶元素,将栈顶元素出栈放入到集合中,再和栈顶元素比较
    3.遇到左右括号
    3.1如果为左括号,直接加入栈
    3.2如果为右括号,依次将栈顶元素弹出,直到左括号,并将左括号弹出
    4.最后将栈顶元素依次弹出

中缀表达式转换为后缀表达式的过程

以运算算式 3-(4-3)/5*10
以中缀表达式表示为 3 - 4 - 3 / 5 * 10
后缀表达式表示为 3 4 3 - 5 / 10 * -
其中涉及到了栈的入栈和出栈,

转化过程:
1.先创建一个集合,用于记录后缀表达式,创建一个栈,用于记录运算符
2.3进入集合,-进入栈 集合 3 栈 -
3.左括号进栈 集合3 栈- (
4.4进入集合,-进入栈,与此时的栈顶元素比较,此时栈顶元素是左括号,-直接放入栈中 集合3 4 栈 - ( -
5.3进入集合,-和栈顶元素比较,优先级相等或者小于,将栈顶元素-弹出 集合中为 3 4 3 - 栈-(
6.右括号进栈 将栈顶元素弹出,直到左括号并弹出左括号 集合3 4 3 - 栈-
7./进栈,优先级大于-,直接进栈,5 进入集合 集合3 4 3 - 5 栈- /
8.*进栈,优先等于栈顶元素,弹出栈顶元素,10进入集合 集合 3 4 3 - 5 / 10 栈 - *
9.算式获取完成,将栈中元素全部弹出 集合 3 4 3 - 5 / 10 * -

实现

整数算式运算

  1. package com.prettyspider.calculate;
  2. import java.util.*;
  3. /**
  4. * @author prettyspider
  5. * @ClassName CalcInt
  6. * @description: 传入整数运算字符串,计算其数值
  7. * @date 2023/9/11 6:21
  8. * @Version V1.0
  9. */
  10. public class CalcInt {
  11. /**
  12. * 中缀表达式转化成后缀表达式
  13. * <p>
  14. * 1.遇到操作数直接放入到集合中
  15. * 2.遇到操作符
  16. * 2.1当栈为空,或栈顶元素为(,直接放入到栈中
  17. * 2.2当优先级比栈顶元素高时,直接进栈
  18. * 2.3当优先级小于等于栈顶元素,将栈顶元素出栈放入到集合中,再和栈顶元素比较
  19. * 3.遇到左右括号
  20. * 3.1如果为左括号,直接加入栈
  21. * 3.2如果为右括号,依次将栈顶元素弹出,直到左括号,并将左括号弹出
  22. * 4.最后将栈顶元素依次弹出
  23. */
  24. public static void main(String[] args) {
  25. // 要传入的运算字符串
  26. String s = "10+2*(3-4)+20*(3*4+3)/3+20";
  27. // 中缀表达式转化成后缀表达式
  28. List<String> list = inToPastExpression(s);
  29. // 计算
  30. int number = calc(list);
  31. System.out.println("计算的数值为"+number);
  32. }
  33. /**
  34. * 计算后缀表达式
  35. * @param list 后缀表达式集合
  36. * @return 传入的整数运算字符串的计算数值
  37. */
  38. private static int calc(List<String> list) {
  39. // 创建栈,用于记录数据
  40. Stack<String> stack = new Stack<>();
  41. for (String s : list) {
  42. // 1.放入 当是数值时,直接放入栈中
  43. if (s.matches("\\d+")) {
  44. stack.push(s);
  45. } else {
  46. // 2.去除 当是运算符时,再栈中取出两个数,进行运算,并将运算之后的结果放入到栈中
  47. int num1, num2;
  48. switch (s) {
  49. case "+":
  50. num1 = Integer.parseInt(stack.pop());
  51. num2 = Integer.parseInt(stack.pop());
  52. stack.push(String.valueOf(num2 + num1));
  53. break;
  54. case "-":
  55. num1 = Integer.parseInt(stack.pop());
  56. num2 = Integer.parseInt(stack.pop());
  57. stack.push(String.valueOf(num2 - num1));
  58. break;
  59. case "*":
  60. num1 = Integer.parseInt(stack.pop());
  61. num2 = Integer.parseInt(stack.pop());
  62. stack.push(String.valueOf(num2 * num1));
  63. break;
  64. case "/":
  65. num1 = Integer.parseInt(stack.pop());
  66. num2 = Integer.parseInt(stack.pop());
  67. stack.push(String.valueOf(num2 / num1));
  68. break;
  69. }
  70. }
  71. }
  72. return Integer.parseInt(stack.pop());
  73. }
  74. /**
  75. * 将传入的字符串进行切分,存放到集合中
  76. * @param str 传入的算数字符串
  77. * @return 后缀表达式集合
  78. */
  79. private static List<String> inToPastExpression(String str) {
  80. // 创建集合和栈
  81. List<String> list = new ArrayList<>();
  82. Stack<String> stack = new Stack<>();
  83. // 切分数据
  84. ArrayList<String> arr = cutString(str);
  85. for (int i = 0; i < arr.size(); i++) {
  86. // * 1.遇到操作数直接放入到集合中
  87. String value = arr.get(i);
  88. if (value.matches("\\d+")) {
  89. list.add(value);
  90. }
  91. // * 3.遇到左右括号
  92. // * 3.1如果为左括号,直接加入群
  93. else if ("(".equals(value)) {
  94. stack.push(value);
  95. }
  96. // * 3.2如果为右括号,依次将栈顶元素弹出,直到左括号,并将左括号弹出
  97. else if (")".equals(value)) {
  98. while (!"(".equals(stack.peek())) {
  99. list.add(stack.pop());
  100. }
  101. stack.pop();
  102. }
  103. // * 2.遇到操作符
  104. else {
  105. // * 2.1当栈为空,或栈顶元素为(,直接放入到栈中
  106. // * 2.3当优先级小于等于栈顶元素,将栈顶元素出栈放入到集合中,再和栈顶元素比较
  107. while (stack.size() != 0 && !judgeOperator(value, stack.peek())) {
  108. list.add(stack.pop());
  109. }
  110. // * 2.2当优先级比栈顶元素高时,直接进栈
  111. stack.push(value);
  112. }
  113. }
  114. // * 4.最后将栈顶元素依次弹出
  115. while (!stack.empty()) {
  116. list.add(stack.pop());
  117. }
  118. System.out.println(list);
  119. return list;
  120. }
  121. /**
  122. * 将运算字符切分切分成集合
  123. * @param str 待切分字符串
  124. * @return ArrayList<String> 被切分之后的字符串集合
  125. */
  126. private static ArrayList<String> cutString(String str) {
  127. String[] arr = new String[str.length()-1];
  128. // 为字符串数组赋初值
  129. for (int i = 0; i < arr.length; i++) {
  130. arr[i] = "";
  131. }
  132. int index = 0;
  133. arr[index] = String.valueOf(str.charAt(0));
  134. for (int i = 1; i < str.length(); i++) {
  135. // 1.是数值放入
  136. if (Character.isDigit(str.charAt(i))) {
  137. // 1.1并看其起一个数据是不是也数值,是数值,将其前一个数据*10+本身
  138. if (arr[index].matches("\\d+")) {
  139. arr[index] = String.valueOf(Integer.parseInt(arr[index]) * 10 + Integer.parseInt(String.valueOf(str.charAt(i))));
  140. } else {
  141. arr[++index] = String.valueOf(str.charAt(i));
  142. }
  143. } else {
  144. // 2.不是数值,直接加入到集合中
  145. arr[++index] = String.valueOf(str.charAt(i));
  146. }
  147. }
  148. // 去除null
  149. ArrayList<String> list = removeNull(arr);
  150. return list;
  151. }
  152. /**
  153. * 将字符串数组中为空的元素去除
  154. * @param arr 字符串数组
  155. * @return 去除控制的字符串集合
  156. */
  157. private static ArrayList<String> removeNull(String[] arr) {
  158. ArrayList<String> list = new ArrayList<>();
  159. for (String s : arr) {
  160. if (!"".equals(s)) {
  161. list.add(s);
  162. }
  163. }
  164. return list;
  165. }
  166. /**
  167. * 比较新旧操作符的权重,判断是存放还是取出
  168. * @param s1 新的操作符
  169. * @param s2 旧的操作符
  170. * @return 新旧操作符的权重比较
  171. */
  172. private static boolean judgeOperator(String s1, String s2) {
  173. int num1 = 0, num2 = 0;
  174. switch (s1) {
  175. case "+":
  176. case "-":
  177. num1 = 1;
  178. break;
  179. case "*":
  180. case "/":
  181. num1 = 2;
  182. break;
  183. }
  184. switch (s2) {
  185. case "+":
  186. case "-":
  187. num2 = 1;
  188. break;
  189. case "*":
  190. case "/":
  191. num2 = 2;
  192. break;
  193. }
  194. // 判断新旧操作符的优先级
  195. if (num1 > num2) {
  196. return true;
  197. }
  198. return false;
  199. }
  200. }

小数算式运算

小数运算要考虑小数点,相对于整数要比较的复杂
在获取娴熟时,需要对小数点的位置进行判断,对小数点右边的数据进行处理

  1. package com.prettyspider.calculate;
  2. import java.util.*;
  3. /**
  4. * @author prettyspider
  5. * @ClassName CalcInt
  6. * @description: 传入整数运算字符串,计算其数值
  7. * @date 2023/9/11 6:21
  8. * @Version V1.0
  9. */
  10. public class CalcDouble {
  11. /**
  12. * 中缀表达式转化成后缀表达式
  13. * <p>
  14. * 1.遇到操作数直接放入到集合中
  15. * 2.遇到操作符
  16. * 2.1当栈为空,或栈顶元素为(,直接放入到栈中
  17. * 2.2当优先级比栈顶元素高时,直接进栈
  18. * 2.3当优先级小于等于栈顶元素,将栈顶元素出栈放入到集合中,再和栈顶元素比较
  19. * 3.遇到左右括号
  20. * 3.1如果为左括号,直接加入栈
  21. * 3.2如果为右括号,依次将栈顶元素弹出,直到左括号,并将左括号弹出
  22. * 4.最后将栈顶元素依次弹出
  23. */
  24. public static void main(String[] args) {
  25. // 要传入的运算字符串
  26. String s = "10.32*3.23+2.234";
  27. // 中缀表达式转化成后缀表达式
  28. List<String> list = inToPastExpression(s);
  29. // 计算
  30. double number = calc(list);
  31. System.out.println("计算的数值为"+number);
  32. }
  33. /**
  34. * 计算后缀表达式
  35. * @param list 后缀表达式集合
  36. * @return 传入的整数运算字符串的计算数值
  37. */
  38. private static double calc(List<String> list) {
  39. // 创建栈,用于记录数据
  40. Stack<String> stack = new Stack<>();
  41. for (String s : list) {
  42. // 1.放入 当是数值时,直接放入栈中
  43. if (s.matches("(\\d|\\.)+")) {
  44. stack.push(s);
  45. } else {
  46. // 2.去除 当是运算符时,再栈中取出两个数,进行运算,并将运算之后的结果放入到栈中
  47. double num1, num2;
  48. switch (s) {
  49. case "+":
  50. num1 = Double.valueOf(stack.pop());
  51. num2 = Double.valueOf(stack.pop());
  52. stack.push(String.valueOf(num2 + num1));
  53. break;
  54. case "-":
  55. num1 = Double.valueOf(stack.pop());
  56. num2 = Double.valueOf(stack.pop());
  57. stack.push(String.valueOf(num2 - num1));
  58. break;
  59. case "*":
  60. num1 = Double.valueOf(stack.pop());
  61. num2 = Double.valueOf(stack.pop());
  62. stack.push(String.valueOf(num2 * num1));
  63. break;
  64. case "/":
  65. num1 = Double.valueOf(stack.pop());
  66. num2 = Double.valueOf(stack.pop());
  67. stack.push(String.valueOf(num2 / num1));
  68. break;
  69. }
  70. }
  71. }
  72. return Double.valueOf(stack.pop());
  73. }
  74. /**
  75. * 将传入的字符串进行切分,存放到集合中
  76. * @param str 传入的算数字符串
  77. * @return 后缀表达式集合
  78. */
  79. private static List<String> inToPastExpression(String str) {
  80. // 创建集合和栈
  81. List<String> list = new ArrayList<>();
  82. Stack<String> stack = new Stack<>();
  83. // 切分数据
  84. ArrayList<String> arr = cutString(str);
  85. for (int i = 0; i < arr.size(); i++) {
  86. // * 1.遇到操作数直接放入到集合中
  87. String value = arr.get(i);
  88. if (value.matches("(\\d|\\.)+")) {
  89. list.add(value);
  90. }
  91. // * 3.遇到左右括号
  92. // * 3.1如果为左括号,直接加入群
  93. else if ("(".equals(value)) {
  94. stack.push(value);
  95. }
  96. // * 3.2如果为右括号,依次将栈顶元素弹出,直到左括号,并将左括号弹出
  97. else if (")".equals(value)) {
  98. while (!"(".equals(stack.peek())) {
  99. list.add(stack.pop());
  100. }
  101. stack.pop();
  102. }
  103. // * 2.遇到操作符
  104. else {
  105. // * 2.1当栈为空,或栈顶元素为(,直接放入到栈中
  106. // * 2.3当优先级小于等于栈顶元素,将栈顶元素出栈放入到集合中,再和栈顶元素比较
  107. while (stack.size() != 0 && !judgeOperator(value, stack.peek())) {
  108. list.add(stack.pop());
  109. }
  110. // * 2.2当优先级比栈顶元素高时,直接进栈
  111. stack.push(value);
  112. }
  113. }
  114. // * 4.最后将栈顶元素依次弹出
  115. while (!stack.empty()) {
  116. list.add(stack.pop());
  117. }
  118. return list;
  119. }
  120. /**
  121. * 将运算字符切分切分成集合
  122. * @param str 待切分字符串
  123. * @return ArrayList<String> 被切分之后的字符串集合
  124. */
  125. private static ArrayList<String> cutString(String str) {
  126. String[] arr = new String[str.length()-1];
  127. // 为字符串数组赋初值
  128. for (int i = 0; i < arr.length; i++) {
  129. arr[i] = "";
  130. }
  131. int index = 0;
  132. arr[index] = String.valueOf(str.charAt(0));
  133. for (int i = 1; i < str.length(); i++) {
  134. // 1.是数值放入
  135. if (Character.isDigit(str.charAt(i))|| ".".equals(String.valueOf(str.charAt(i)))) {
  136. // 1.1并看其起一个数据是不是也数值,是数值,将其前一个数据拼接
  137. if (arr[index].matches("(\\d|\\.)+")) {
  138. arr[index] = arr[index] + str.charAt(i);
  139. } else {
  140. arr[++index] = String.valueOf(str.charAt(i));
  141. }
  142. } else {
  143. // 2.不是数值,直接加入到集合中
  144. arr[++index] = String.valueOf(str.charAt(i));
  145. }
  146. }
  147. // 去除null
  148. ArrayList<String> list = removeNull(arr);
  149. return list;
  150. }
  151. /**
  152. * 将字符串数组中为空的元素去除
  153. * @param arr 字符串数组
  154. * @return 去除控制的字符串集合
  155. */
  156. private static ArrayList<String> removeNull(String[] arr) {
  157. ArrayList<String> list = new ArrayList<>();
  158. for (String s : arr) {
  159. if (!"".equals(s)) {
  160. list.add(s);
  161. }
  162. }
  163. return list;
  164. }
  165. /**
  166. * 比较新旧操作符的权重,判断是存放还是取出
  167. * @param s1 新的操作符
  168. * @param s2 旧的操作符
  169. * @return 新旧操作符的权重比较
  170. */
  171. private static boolean judgeOperator(String s1, String s2) {
  172. int num1 = 0, num2 = 0;
  173. switch (s1) {
  174. case "+":
  175. case "-":
  176. num1 = 1;
  177. break;
  178. case "*":
  179. case "/":
  180. num1 = 2;
  181. break;
  182. }
  183. switch (s2) {
  184. case "+":
  185. case "-":
  186. num2 = 1;
  187. break;
  188. case "*":
  189. case "/":
  190. num2 = 2;
  191. break;
  192. }
  193. // 判断新旧操作符的优先级
  194. if (num1 > num2) {
  195. return true;
  196. }
  197. return false;
  198. }
  199. }

在获取数据时,巧妙的使用正则获取对应的数据,利于数据的处理

原文链接:https://www.cnblogs.com/prettyspider/p/17703787.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号