数据结构——逆波兰式

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

逆波兰式

逆波兰式(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.

原文地址:https://www.cnblogs.com/dojo-lzz/p/9000223.html

时间: 2024-10-11 06:32:24

数据结构——逆波兰式的相关文章

将中缀式转化为逆波兰式 (栈)

逆波兰式:Reverse Polish notation,RPN,,也叫后缀表达式,将运算符写在操作数之后 数据结构:两个栈S1和S2.S1临时存储运算符,S2存储最终结果. 算法:(1)若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈(2)若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符优先级大于S1栈栈顶运算符优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,则将该运算符送入

栈的应用-逆波兰式

---恢复内容开始--- 普通的计算方式,也叫中缀表达式计算机识别及正确运用需要耗费大量的资源 23+45*2-(8+2) 计算机想要正确计算出此时的结果需要十分复杂,更何况情况十分多变. 逆波兰式:又叫做后缀表达式,它能去除中缀表达式的括号.十分符合计算机的计算思维,能极大提高效率 表达式不能用字符串进行存储,因为这将无法分辨,应用集合(ArrayList,LinkedList存储) 23 45 2 * 8 2 + - + 那么中缀表达式是如何变成后缀表达式的呢?原则如下: 1.首先把普通的表

HDU1237 简单计算器 【栈】+【逆波兰式】

版本:1.0 日期:2014.5.17 2014.6.1 版权:© 2014 kince 转载注明出处 在介绍SwitchButton之前,先来看一下系统Button是如何实现的.源码如下: @RemoteView public class Button extends TextView { public Button(Context context) { this(context, null); } public Button(Context context, AttributeSet att

使用逆波兰式进行表达式求值

中缀表达式及后缀表达式图解中说明了使用逆波兰式进行表达式求值的方法,这里使用C++进行实现.实现和原理讲解有一点不同,需要进一步进行细化. 关于将中缀表达式转换成后后缀表达式的规则: 规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分:若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于找顶符号(乘除优先加减)则栈顶元素依次出找并输出,并将当前符号进栈,一直到最终输出后缀表达式为止. 上面的规则转换成下面的执行规则: 1.遇到操作数:直接输出(添加到后缀

逆波兰式与表达式求解

/*************** 逆波兰式即后缀表示法 预处理 ---- 中序表达式->逆序表达式(infix to postfix) 算法: while(表达式非空) if (遇到操作数) 直接输出 else if (遇到操作符op) op是( 直接入栈s op是) s.push输出,直到( op是四则运算,则 if (s为空 || s.top为( || op 优先级高于 s.top) op 入栈 else s.push输出 if (!s.empty) s.push输出 计算 算法: if (

python 逆波兰式

逆波兰式,也叫后缀表达式 技巧:为简化代码,引入一个不存在的运算符#,优先级最低.置于堆栈底部 class Stack(object): '''堆栈''' def __init__(self): self._stack = [] def pop(self): return self._stack.pop() def push(self, x): self._stack.append(x) 一.表达式无括号 def solve(bds): '''不带括号,引入#运算符''' pro = dict(

逆波兰式,有关栈的问题

/*有关逆波兰式的问题,也叫后缀表达式(将运算符写在操作数之后):例如:a+b的逆波兰式为a b +;下列程序是有关数字与数字相加的,将程序简单理解为,如果是数字,那么压入栈中,如果是运算符,那么出栈,将此前压入栈中的两个数取出栈并且相加,相加后再压入栈中,以此类推(按运算符的优先级!)*/ #include <stdio.h> #include <math.h>#include <stdlib.h>#include <ctype.h>#define STA

Haskell解决逆波兰式

摘自<Haskell趣学指南- Learn You a Haskell for Great Good> {- 逆波兰式(revese polish notation, RPN): 操作符出现在操作数的后面,而不是夹在它们中间. 如我们使用 "4 3 +" 而不是 "4 + 3". -} solveRPN :: String -> Double solveRPN = head . foldl foldingFunction [] . words wh

逆波兰式

在程序设计中,可能碰到需要对字符串数学表达式求值的问题,常用的方法是解析表达式,生成二叉树,然后进行计算.编译器就是使用这种方法来解析程序中的表达式的.这种方法实现起来有点难度,需要考虑运算符的优先级,括号的配对,堆栈的使用等等.我们正常情况下看到的数学表达式如果用二叉树遍历的话,恰好是中序遍历,故叫做中序表达式.除此之外,还有前序表达式,后序表达式.如:a+b+c(中序),++abc(前序),ab+c+(后序),如果表达式含有×,/,()等就更复杂了. 后缀表达式也称逆波兰表达式 因其使表达式