• 数据结构与算法(二)——前缀、中缀、后缀表达式


    一、前缀表达式(波兰表达式)

    1.1、计算机求值

    从右至左扫描表达式,遇到数字时,将数字压入堆栈。遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 和 次顶元素),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果

    例如:
    (3+4)×5-6 对应的前缀表达式就是 - × + 3 4 5 6。针对前缀表达式求值步骤如下:
    1)从右至左扫描,将 6、5、4、3压入堆栈;
    2)遇到 + 运算符,因此弹出3和4(3为栈顶元素,4为次顶元素),计算出3+4的值,得 7,再将7入栈;
    3)接下来是×运算符,因此弹出 7 和 5,计算出 7×5=35,将35入栈;
    4)最后是 - 运算符,计算出 35-6 的值,即 29,由此得出最终结果。

    二、中缀表达式

    1)中缀表达式就是常见的运算符表达式,如 (3+4)×5-6
    2)中缀表达式的求值是我们人类最熟悉的,但是对计算机来说缺不好操作。因此,在计算结果时,往往会将中缀表达式转成其他表达式来操作(一般转成后缀表达式)
    中缀表达式 - 栈实现综合计算器

    三、后缀表达式(逆波兰表达式)

    1)后缀表达式又称 逆波兰表达式,与前缀表达式相近,只是运算符位于操作数之后;
    2)举例说明:(3+4)×5-6 对应的后缀表达式就是 3 4 + 5 × 6 -
    3)在这里插入图片描述

    3.1、计算机求值

    从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到操作符时,弹出栈顶的两个数,用运算符对它们做出相应的计算(次顶元素 和 栈顶元素),并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果。

    例如:
    (3+4)×5-6 对应的后缀表达式就是 3 4 + 5 × 6 -。针对后缀表达式求值步骤如下:
    1)从左至右扫描,将 3 和 4 压入堆栈;
    2)遇到 + 运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得 7,再将7入栈;
    3)接下来是×运算符,因此弹出 5 和 7,计算出 7×5=35,将35入栈;
    4)将 6 入栈
    5)最后是 - 运算符,计算出 35-6 的值,即 29,由此得出最终结果。

    3.2、逆波兰计算器分析与实现

    我们完成一个逆波兰计算器,要求完成如下任务:
    1)输入一个逆波兰表达式(后缀表达式),使用栈(Stack), 计算其结果
    2)支持小括号和多位数整数,因为这里我们主要讲的是数据结构,因此计算器进行简化,只支持对整数的计算。
    3)思路分析
    4)代码完成

    package Algotithm.stack
    
    import java.util
    
    object PolandNotation {
      def main(args: Array[String]): Unit = {
        //先定义一个逆波兰表达式
        val suffixExpression = "3 4 + 5 * 6 -"
        //思路
        //1.先将 suffixExpression => arrayList中
        //2.将arrayList传递给一个方法,遍历 配合栈完成计算
        val list = getListString(suffixExpression)
        println(list)
        val result = calculate(list)
        println(s"计算的结果时$result")
      }
    
      //将逆波兰表达式,依次将数据和运算符 放入到arrayList中
      def getListString(string: String): util.ArrayList[String] = {
        //将arrayList分割
        val strings = string.split(" ")
        val list = new util.ArrayList[String]()
        strings.foreach(elem => list.add(elem))
        list
      }
    
      //完成计算
      def calculate(list: util.ArrayList[String]): Int = {
        //创建栈,只需要一个栈即可
        val stack = new util.Stack[String]
        //遍历ls
        list.forEach(elem => {
          if (elem.matches("\\d+")) { //匹配的是多位数
            //入栈
            stack.push(elem)
          } else {
            //pop出两个数,并运算,再入栈
            val num2 = stack.pop().toInt
            val num1 = stack.pop().toInt
            val res = elem match {
              case "+" => num1 + num2
              case "-" => num1 - num2
              case "*" => num1 * num2
              case "/" => num1 / num2
              case _ => throw new Exception("运算符有误")
            }
            stack.push(res.toString)
          }
        })
        //最后留在stack中的就是运算结果
        stack.pop().toInt
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    在这里插入图片描述

    四、中缀转后缀表达式

    具体步骤如下:
    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中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式

    方法:将 中缀表达式转成对应的List

      //方法:将 中缀表达式转成对应的List
      def toInfixExpressionList(s: String): util.ArrayList[String] = {
        //定义一个list,存放中缀表达式 对应的内容
        val ls = new util.ArrayList[String]()
        var i = 0 //指针,用于遍历 ls
        var str = "" //对多位数的拼接
        var c: Char = 0 //每遍历到一个字符,就放入到c
        while (i < s.length) {
          //如果c是一个非数字,需要加入到ls
          c = s.charAt(i)
          if (s.charAt(i) < 48 || s.charAt(i) > 57) {
            ls.add("" + s.charAt(i))
            i += 1
          } else { //如果是一个数,需要考虑多位数
            str = ""
            while (i < s.length && s.charAt(i) >= 48 && s.charAt(i) <= 57) {
              str += s.charAt(i)
              i += 1
            }
            ls.add(str)
          }
        }
        ls
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这里插入图片描述

    注意:在 while 中必须要加该判断,否则会下标越界异常。

    定义优先级

    class Operation {
      private val ADD = 1
      private val SUB = 1
      private val MUL = 2
      private val DIV = 2
    
      //返回对应的优先级数字
      def getValue(operation: String): Int = {
        val result = operation match {
          case "+" => ADD
          case "-" => SUB
          case "*" => MUL
          case "/" => DIV
          case _ => 0
        }
        result
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    中缀表达式list => 后缀表达式list

      //方法:将得到的中缀表达式list => 后缀表达式list
      def parseSuffixExpressionList(ls: List[String]): util.ArrayList[String] = {
        //定义两个栈
        val s1 = new util.Stack[String] //符号栈
        //说明:因为s2在整个操作中没有pop,且后面还要逆序输出
        //因此不用stack,直接使用list
        val s2 = new util.ArrayList[String] //存储中间结果的list
    
        //遍历ls
        ls.foreach(elem => {
          //如果是一个数,入s2
          if (elem.matches("\\d+")) {
            s2.add(elem)
          } else if (elem.equals("(")) {
            s1.push(elem)
          } else if (elem.equals(")")) {
            //如果是")",则依次弹出s1栈顶的运算符并压入s2,直到遇到"("
            while (!s1.peek().equals("(")) {
              s2.add(s1.pop())
            }
            s1.pop() //将 ( 弹出s1栈,消除括号
          } else {
            //当elem运算符优先级 ≤ s1栈顶运算符
            //比较优先级高低的方法
            while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(elem)) {
              s2.add(s1.pop())
            }
            //将elem压入栈中
            s1.push(elem)
          }
        })
        //将s1剩余的运算符依次弹出并加入s2
        while (s1.size() != 0) {
          s2.add(s1.pop())
        }
        s2  //因为是存放到list,因此按顺序输出就是后缀表达式
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    主函数

    def main(args: Array[String]): Unit = {
    
        //完成将一个中缀表达式转成后缀表达式的功能
        //说明
        //1.1+((2+3)*4)-5 => 1 2 3 + 4 * 5 -
        //2.因为直接对str操作不方便,因此先将 str 转成中缀的list
        //3.将得到的中缀表达式list => 后缀表达式list
        val expression = "1+((2+3)*4)-5"
        val infixExpressionList = toInfixExpressionList(expression)
        println(s"$infixExpressionList")
        val suffixExpressionList = parseSuffixExpressionList(infixExpressionList)
        println(s"对应的后缀表达式 => $suffixExpressionList")
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

  • 相关阅读:
    ROS2时间同步(python)
    http 请求 Cros 跨域问题记录(转)
    在UMG中播放图像序列,出现卡帧怎么办?
    vba 获取PPT幻灯片中的所有标题的代码
    STM32使用HAL库UART接收不定长数据-1
    YOLO目标检测——钢表面缺陷检测数据集下载分享【含对应voc、coco和yolo三种格式标签】
    IMX6ULL移植篇-Linux内核源码目录分析一
    Redis可视化工具-Another Redis Desktop Manager 安装
    LVS-NAT之VMNET环境搭建
    PTA 7-190 猴子选大王
  • 原文地址:https://blog.csdn.net/d905133872/article/details/132676698