计算器思想-中缀表达式转化为后缀表达式

打印 上一主题 下一主题

主题 912|帖子 912|积分 2738

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

对于一个算式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. }
复制代码
在获取数据时,巧妙的使用正则获取对应的数据,利于数据的处理

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

南七星之家

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表