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

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

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)数据结构与算法(二)——前缀、中缀、后缀表达式_第1张图片

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
  }
}

数据结构与算法(二)——前缀、中缀、后缀表达式_第2张图片

四、中缀转后缀表达式

具体步骤如下:
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
  }

数据结构与算法(二)——前缀、中缀、后缀表达式_第3张图片

注意:在 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
  }
}

中缀表达式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,因此按顺序输出就是后缀表达式
  }

主函数

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")
  }

数据结构与算法(二)——前缀、中缀、后缀表达式_第4张图片

你可能感兴趣的:(#,算法与数据结构,数据库,链表,算法)