zoukankan      html  css  js  c++  java
  • 数据结构——逆波兰式

    很久没有关注算法和数据结构,大部分知识都已经忘记了;是时间好好回炉一下了,说实话干读数据机构这本书还是挺枯燥而且这本书原理性比较多,有一定的难度。这不刚看到逆波兰式废了好大劲才搞懂,老了。。。

    逆波兰式

    逆波兰式(Reverse Polish notation,RPN,或逆波兰记法),也叫后缀表达式(将运算符写在操作数之后)

    一个表达式E的后缀形式可以如下定义:
    (1)如果E是一个变量或常量,则E的后缀式是E本身。
    (2)如果E是E1 op E2形式的表达式,这里op是如何二元操作符,则E的后缀式为E1'E2' op,这里E1'和E2'分别为E1和E2的后缀式。
    (3)如果E是(E1)形式的表达式,则E1的后缀式就是E的后缀式。
    如:我们平时写a+b,这是中缀表达式,写成后缀表达式就是:ab+
    (a+b)*c-(a+b)/e的后缀表达式为:
    (a+b)*c-(a+b)/e
    →((a+b)*c)((a+b)/e)-
    →((a+b)c*)((a+b)e/)-
    →(ab+c*)(ab+e/)-
    →ab+c*ab+e/-

    算法实现

    将一个普通的中序表达式转换为逆波兰表达式的一般算法是:
    首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为输入逆波兰式的栈S2(空栈),S1栈可先放入优先级最低的运算符#,注意,中缀式应以此最低优先级的运算符结束。可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤:
    (1)若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈
    (2)若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符优先级(不包括括号运算符)大于S1栈栈顶运算符优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,最后将该运算符送入S1栈。
    (3)若取出的字符是“(”,则直接送入S1栈顶。
    (4)若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。
    (5)重复上面的1~4步,直至处理完所有的输入字符
    (6)若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。
    完成以上步骤,S2栈便为逆波兰式输出结果。不过S2应做一下逆序处理。便可以按照逆波兰式的计算方法计算了!
     

    代码程序

    //'1 + 2 * 3 + (4 * 5 + 6) * 7'
    
    function ReversePolish() {
      this.operatorStack = [];
      // this.operator = ['+', '-', '*', '/', '(', ')'];
      this.operator = {
        '+': 1,
        '-': 1,
        '*': 2,
        '/': 2,
        '(': 10,
        ')': 10
      };
      this.rp = [];
    }
    
    ReversePolish.prototype.convert = function(str) {
      debugger;
      // ('15 + 2 * 3 + (4 * 5 + 6) * 7').trim().replace(/s+/g, '').split(/([+|-|*|/|(|)])/)
      // ["15", "+", "2", "*", "3", "+", "", "(", "4", "*", "5", "+", "6", ")", "", "*", "7"]
      str
      .trim()
      .replace(/s+/g, '')
      .split(/([+|-|*|/|(|)])/)
      .filter(e => !!e)
      .forEach(e => {
        if (/[0-9]/g.test(e)) { // 数字直接放入逆波兰式数组
          this.rp.push(e);
        } else {
          if (this.operatorStack.length === 0) {// 操作符栈为空直接压入栈
            this.operatorStack.push(e);
          } else {
            if (e === '(') { // 左括号直接入栈
              this.operatorStack.push(e);
            } else if (e === ')') { // 右括号弹出所有的操作符进入逆波兰数组,直至遇到 (, (不进入逆波兰数组
              let op = this.operatorStack.pop();
              while(op !== '(') {
                this.rp.push(op);
                op = this.operatorStack.pop();
              }
              // this.operatorStack.pop();
            } else { // 遇到其他操作符则弹出所有栈顶元素,直至遇到优先级更低的操作符,但是不处理(
              let op = this.operatorStack.pop();
              while(op && this.operator[op] >= this.operator[e] && op !== '(') {
                this.rp.push(op);
                op = this.operatorStack.pop();
              }
              if (op) {
                this.operatorStack.push(op);
              }
              this.operatorStack.push(e);
            }
          }
        }
      });
      // 运行结束后将所有的操作符栈弹出
      let op = this.operatorStack.pop();
      while(op) {
        this.rp.push(op);
        op = this.operatorStack.pop();
      }
    
      console.log(this.rp.join(' '));
    };
    
    //15 2 3 * + 4 5 * 6 + 7 * +
    ReversePolish.prototype.eval = function(){
      let numberStack = [];
      this.rp.forEach(e => {
        if (/[0-9]/g.test(e)) {
          numberStack.push(Number(e));
        } else if (this.operator[e]) {
          let n2 = numberStack.pop();
          let n1 = numberStack.pop();
          switch(e) {
            case '+':
              numberStack.push(n1 + n2);
              break;
            case '-':
              numberStack.push(n1 - n2);
              break;
            case '*':
              numberStack.push(n1 * n2);
              break;
            case '/':
              numberStack.push(n1 / n2);
          }
        }
      });
      return numberStack.pop();
    }
    
    let rp = new ReversePolish();
    rp.convert('15 + 2 * 3 + (4 * 5 + 6) * 7');
    rp.eval();

      感觉逆波兰式不仅是一种方法,更是一种思想,逆波兰式这种计算方法没有必要知道任何运算符优先规则。就像我们实际业务中有很多逻辑判断、各种优先级的场景,是否也可以使用逆波兰式的思想来解决?上面的例子也是比较简单的情况,没有考虑运算符的执行顺序,对于2^2^3这个种,实际是等于2^8等于256,而不是4^3=64.

  • 相关阅读:
    算法之美_源码公布(5)
    SDL2源码分析2:窗体(SDL_Window)
    hdu5303Delicious Apples
    Android之怎样给ListView加入过滤器
    EntboostChat 0.9(越狱版)公布,iOS免费企业IM
    unix关于打包命令zip的使用
    用 query 方法 获得xml 节点的值
    用友ERP T6技术解析(六) 库龄分析
    [笔试题] 两个有趣的问题
    使用SecueCRT在本地主机与远程主机之间交互文件
  • 原文地址:https://www.cnblogs.com/dojo-lzz/p/9000223.html
Copyright © 2011-2022 走看看