【算法】表达式求值--逆波兰算法介绍

用逆波兰表达式的三角函数计算器 #include#include #include #include #include #include #include"caltulator.h" using namespace std; typedef struct STK { double num=889277; char note=0; }stk; char s[1000];//初始输入的数组 stk s1[1000];//字符 阅读详情

【算法】表达式求值--逆波兰算法介绍

转自:https://www.cnblogs.com/lulipro/p/7450886.html

逆波兰算法介绍

假定给定一个只 包含 加、减、乘、除,和括号的算术表达式,你怎么编写程序计算出其结果?

问题是:在表达式中,括号,以及括号的多层嵌套 的使用,运算符的优先级不同等因素,使得一个算术表达式在计算时,运算顺序往往因表达式的内容而定,不具规律性。 这样很难编写出统一的计算指令。
使用逆波兰算法可以轻松解决这个问题。他的核心思想是将普通的中缀表达式转换为后缀表达式。

什么是中缀表达式?例如a+b,运算符在两个操作数的中间。这是我们从小学开始学习数学就一直使用的表达式形式。

什么是后缀表达式?例如a b + ,运算符在两个操作数的后面。后缀表达式虽然看起来奇怪,不利于人阅读,但利于计算机处理。

转换为后缀表达式的好处是:


1、去除原来表达式中的括号,因为括号只指示运算顺序,不是实际参与计算的元素。
2、使得运算顺序有规律可寻,计算机能编写出代码完成计算。

 

算术表达式的组成部分

一个表达式有如下及部分元素组成

  • 操作数:可以是任何数值:1,89 , 3.14159 ...
  • 运算符:

    分为单目运算符 如 sin , cos , 双目运算符 如 加、减、乘、除 。

    运算符还会分为左结合性和右结合性。同级的左结合性的运算符从左往右依次计算。同级的右结合性的运算符从右往左依次计算。 
    如: 7-6+1 等价于 (7-6) + 1 ,因为普通加减运算符是左结合性的。
    如:C语言中的组合赋值语句: a = b = 1 等价于 a = (b=1) ,因为赋值运算符在C中是右结合性的。

       对于单目运算符,还分为前缀运算符和后缀运算符,如 sin(x) 是前缀运算符,而 阶乘运算符 : n ! 就是后缀运算符。

  • 分界符:一般是圆括号 ( ) , 用于指示运算的先后顺序。

在本文中,只会考虑算术表达式 有 加、减、乘、除 运算符, 左右圆括号 ( , ) ,以及合法的数字简单的情况。对于更加复杂的运算符,只要对这个算法轻微修改,就可以支持。

 

逆波兰算法的原理

逆波兰算法的核心步骤就2个:
1、将中缀表达式转换为后缀表达式,例如输入的原始表达式是 3*(5+7) ,转换得到 3 5 7 + *
2、根据后缀表达式,按照特定的计算规则得到最终计算结果

下面详细介绍这个2步的操作。

中缀表达式转换为后缀表达式
你需要设定一个栈SOP,和一个线性表 L 。SOP用于临时存储运算符和左括号分界符( ,L用于存储后缀表达式。
遍历原始表达式中的每一个表达式元素
(1)如果是操作数,则直接追加到 L中。只有 运算符 或者 分界符( 才可以存放到 栈SOP中
(2)如果是分界符
         Ⅰ 如果是左括号 ( , 则 直接压入SOP,等待下一个最近的 右括号 与之配对。
          Ⅱ 如果是右括号),则说明有一对括号已经配对(在表达式输入无误的情况下)。不将它压栈,丢弃它,然后从SOP中出栈,得到元素e,将e依次追加到L里。一直循环,直到出栈元素e 是 左括号 ( ,同样丢弃他。
(3)如果是运算符(用op1表示)
        Ⅰ如果SOP栈顶元素(用op2表示) 不是运算符,则二者没有可比性,则直接将此运算符op1压栈。 例如栈顶是左括号 ( ,或者栈为空。
         Ⅱ 如果SOP栈顶元素(用op2表示) 是运算符 ,则比较op1和 op2的优先级。如果op1 > op2 ,则直接将此运算符op1压栈。
如果不满足op1 > op2,则将op2出栈,并追加到L,重复步骤3。
也就是说,如果在SOP栈中,有2个相邻的元素都是运算符,则他们必须满足:下层运算符的优先级一定小于上层元素的优先级,才能相邻。

最后,如果SOP中还有元素,则依次弹出追加到L后,就得到了后缀表达式。

伪代码:

#将参数中缀表达式expression转为后缀表达式存放在L中,返回L
function infixToSuffix(expression):
{
    for each element in expression        #对表达式中的每一个元素
    {
        if (element 是一个操作数)
        {
            L.append(element)             #将这个元素追加到线性表L后
        }

        else if (element 是 一个运算符)
        {
            While (sop栈 不为空  &&  sop栈顶元素 是一个运算符  && element的优先级 <= sop栈顶运算符元素的优先级 )
            {
                L.append(sop.pop())
            }
            sop.push(element);     
        }

        else if(element 是 一个分界符)
        {
            if (element is  '('  )
            {
                sop.push(element)
            }
            else if( element is  ')'  )
            {
                While (sop栈不为空 &&   sop栈顶元素 不是 '('  )    #将匹配的2个括号之间的栈元素弹出,追加到L
                {
                    L.append( sop.pop() ); 
                }
                if(sop栈不为空 )
                {
                    sop.pop()           #将匹配到的 '(' 弹出丢弃
                }
            }
        }

    }

    While (sop 栈 不为空)       #将sop栈中剩余的所有元素弹出,追加到L后
    {
        L.append(sop.pop())
    }

    return L
}

示例图:

 

 

根据后缀表达式计算得到最终结果

 

执行这步操作时,也需要一个栈scalc,用于存放计算中的操作数。

伪代码:

function suffixToResult(suffix_expression)
{
    for each element in suffix_expression
    {
        if(element 是 操作数)
        {
            scalc.push(element)
        }
        else if(element 是运算符)
        {
            #从栈中弹出2个操作数 num1 和 num2 。注意:后弹出的num2是第一操作数,num1是第二操作数 。
            #因为这里考虑的都是二元运算符,因此需要弹2个元素出来进行运算。
            num1 = scalc.pop()
            num2 = scalc.pop()

            #使用element代表的运算符完成 num2 和 num1 的计算,产生临时结果 temp_value
            temp_value = num2 【element的运算符: +  ,-  ,* , / 】 num1

            #将temp_value压栈
            scalc.push(temp_value)   
        }

        #如果一切正常,最后栈scalc中仅仅只有一个元素,这个元素就是最终的结果
        return sclac.pop()
    }
}

 

示例图

 

代码实现(Java)

1、核心算法实现是 InfixExpression类中的   public SuffixExpression toSuffixExpression()  和 SuffixExpression  类中的  double getResultValue() throws Exception 。

2、并没有实现解析输入的代码,因此算术表达式只能通过硬编码构造。涉及到编译原理的知识,书到用时方恨少,哎。

3、所有的操作数内部使用double存储。

 

package com. lulix86 .calc ;

import java. util .ArrayList ;
import java. util .Deque ;
import java. util .HashMap ;
import java. util .Iterator ;
import java. util .LinkedList ;
import java. util .List ;
import java. util .Map ;
import java. util .regex . Matcher;
import java. util .regex . Pattern;

public class App
{

      public static void main (String [] args )
      {

            InfixExpression expression = new InfixExpression () ;

            //   (1+8)-(3*(4-1))  = 0

            expression .append ( "(") ;
            expression .append ( "1") ;
            expression .append ( "+") ;
            expression .append ( "8") ;
            expression .append ( ")") ;
            expression .append ( "-") ;
            expression .append ( "(") ;
            expression .append ( "3") ;
            expression .append ( "*") ;
            expression .append ( "(") ;
            expression .append ( "4") ;
            expression .append ( "-") ;
            expression .append ( "1") ;
            expression .append ( ")") ;
            expression .append ( ")") ;


            System . out. println( "原始表达式:" + expression );

            try
            {
                 System . out. println( "计算结果是:" + expression. getResultValue () ) ;
            } catch (Exception e)
            {

                 System . out. println( "一定是表达式输入的有问题,请检查后重试" ) ;
            }


      }


}

/**
 * 表达式元素的公父类。表达式元素具体分为:运算符类,操作数类,分界符类。因此这个类有3个子类。 这是一个抽象类。 这个类的作用如下:
 * 1、定义了表达式元素的共同属性:String content,用来存储元素的字符串形式的内容。
 * 2、限定了分界符元素和运算符元素的相关性质:如可取的content值,运算符的优先级等。
 * 2、这个类提供了一些供子类使用的工具函数:isLegalXXXX。
 *
 *
 * @see ExpressionDelimeter
 * @see ExpressionOperator
 * @see ExpressionOperand
 *
 */
abstract class ExpressionElement
{

      // -----------------------------分界符-----------------------------------

      // 表达式中的分界符:左右圆括号
      public static final String LEFT_PARENTHESES = "(" ;
      public static final String RIGHT_PARENTHESES = ")" ;

      protected static boolean isLegalDelimeter (String content)
      {
            if ( LEFT_PARENTHESES .equals ( content) || RIGHT_PARENTHESES . equals( content ))
                 return true ;
            return false ;
      }

      // -----------------------------运算符-----------------------------------
      // 运算符:这里只用到了常见的这4个运算符
      public static final String PLUS = "+" ;
      public static final String MINUS = "-" ;
      public static final String MULTIPLE = "*" ;
      public static final String DIVIDE = "/" ;

      // 将运算符 和 他的 优先级 通过 k-v 对存放到 Map中:运算符的优先级分为2个等级,用数字1 和2代表,数值越大,优先级越高
      protected static final Map <String , Integer> operatorPiority = new HashMap <> ();
      static
      {
            operatorPiority .put ( PLUS, 1) ;
            operatorPiority .put ( MINUS, 1) ;
            operatorPiority .put ( MULTIPLE, 2) ;
            operatorPiority .put ( DIVIDE, 2) ;
      }

      protected static boolean isLegalOperator (String content)
      {
            if ( ! operatorPiority. containsKey (content ))
                 return false ;
            return true ;
      }

      // -----------------------------操作数-----------------------------------
      // 通过正则表达式校验一个字符串是否是合法的数字 。
      protected static boolean isLegalOperand (String content)
      {
            Pattern numberPat = Pattern .compile ( "(\\+|-)?(\\d+\\.)?\\d+" );
            Matcher mat = numberPat .matcher ( content) ;
            if ( ! mat. matches ())
                 return false ;

            return true ;
      }

      protected final String content ;

      protected ExpressionElement ( String content )
      {
            this .content = content ;
      }

      public String getContent()
      {
            return content ;
      }

      @Override
      public String toString()
      {
            return content . toString() ;
      }

}

/**
 * 表达式运算符类。 构造函数私有,不可以自己创建实例,只能用类中预定义的4个静态类对象,分别代表了加减乘除4个运算符。 类对象不可变。
 * 实现了Comparable接口,compareTo方法用来比较2个运算符的优先级。
 *
 */

class ExpressionOperator extends ExpressionElement implements Comparable <ExpressionOperator >
{

      public static final ExpressionOperator OP_MINUS = new ExpressionOperator (ExpressionElement . MINUS) ;
      public static final ExpressionOperator OP_PLUS = new ExpressionOperator (ExpressionElement . PLUS) ;
      public static final ExpressionOperator OP_MULTIPLE = new ExpressionOperator (ExpressionElement . MULTIPLE) ;
      public static final ExpressionOperator OP_DEVIDE = new ExpressionOperator (ExpressionElement . DIVIDE) ;

      // -----------------------------------------------------

      private final int priority ; // 运算符的优先级

      // 不可以在类外实例化运算符,不允许创建其它的新的运算符
      private ExpressionOperator ( String content )
      {
            super (content ) ;

            if ( ! isLegalOperator( content ))
                 throw new IllegalArgumentException( "运算符 " + content + " 不是合法的" );

            this .priority = operatorPiority .get ( content) ;
      }

      public int getPriority()
      {
            return priority ;
      }

      @Override
      public int compareTo( ExpressionOperator other )
      {
            return this . priority - other . priority;
      }

      @Override
      public boolean equals( Object obj )
      {
            if ( obj == null)
                 return false ;
            if ( obj == this)
                 return true ;
            if ( obj .getClass () != this. getClass ())
                 return false ;

            ExpressionOperator other = ( ExpressionOperator) obj;

            return this . getContent() . equals( other .getContent ()) ;
      }

}

/**
 * 表达式操作数类。
 *
 * 使用double作为数字的存储类型。 不可变类型。 实现了Comparable接口,compareTo方法用来比较2个操作数的大小。
 *
 * 因为操作数是不可枚举的,因此这个类可以创建实例。
 */
class ExpressionOperand extends ExpressionElement implements Comparable <ExpressionOperand >
{

      private final double value ;

      public ExpressionOperand ( String content )
      {
            super (content ) ;
            try
            {
                 value = Double. parseDouble (content ) ;
            } catch (NumberFormatException e)
            {
                 throw new IllegalArgumentException( content + " 不是一个合法的数字" );
            }
      }

      public ExpressionOperand ( double value )
      {
            super (Double . toString( value ));
            this .value = value ;
      }

      public double getValue()
      {
            return value ;
      }

      @Override
      public int compareTo( ExpressionOperand other )
      {
            return Double . compare( this .value , other . value) ;
      }

      @Override
      public String toString()
      {
            return Double . toString( this .value ) ;
      }
}

/**
 * 表达式分界符类。 不可变类型。 构造函数私有,不可以自己创建实例,只能用类中预定义的2个静态类对象,分别代表左,右括号。
 *
 */
class ExpressionDelimeter extends ExpressionElement
{

      public static final ExpressionDelimeter DM_LEFT_PARENTHESES = new ExpressionDelimeter (
                 ExpressionElement . LEFT_PARENTHESES) ;
      public static final ExpressionDelimeter DM_RIGHT_PARENTHESES = new ExpressionDelimeter (
                 ExpressionElement . RIGHT_PARENTHESES) ;

      private ExpressionDelimeter ( String content )
      {
            super (content ) ;
            if ( ! isLegalDelimeter( content ))
                 throw new IllegalArgumentException( "分界符 " + content + " 不是合法的" );
      }

      @Override
      public boolean equals( Object obj )
      {
            if ( obj == null)
                 return false ;
            if ( obj == this)
                 return true ;
            if ( obj .getClass () != this. getClass ())
                 return false ;

            ExpressionDelimeter other = ( ExpressionDelimeter) obj ;

            return content . equals( other .content ) ;
      }

}

/**
 * 表达式类。 你可以把这个类看做是一个存储表达式元素的线性表,因此可以使用appendXXX方法来构造一个表达式。
 *
 * 封装了工具函数 infixToSuffix,用于将中缀表达式转换为后缀表达式。
 * 实现了 Iterable接口,可以迭代ExpressionElement元素对象。
 */

abstract class Expression implements Iterable< ExpressionElement >
{

      // ---------------------------------------------------------------------------

      // 使用ArrayList存储表达式元素
      protected final List< ExpressionElement > expression = new ArrayList <>() ;

      // 追加一个表达式元素对象,不可以追加空元素。
      public boolean append( ExpressionElement e )
      {
            if ( e == null)
                 return false ;
            expression .add ( e) ;
            return true ;
      }

      public boolean append( String content )
      {
            switch ( content )
            {
            case ExpressionElement . LEFT_PARENTHESES:
                 expression .add ( ExpressionDelimeter. DM_LEFT_PARENTHESES ) ;
                 break ;
            case ExpressionElement . RIGHT_PARENTHESES:
                 expression .add ( ExpressionDelimeter. DM_RIGHT_PARENTHESES ) ;
                 break ;
            case ExpressionElement . PLUS:
                 expression .add ( ExpressionOperator. OP_PLUS );
                 break ;
            case ExpressionElement . MINUS:
                 expression .add ( ExpressionOperator. OP_MINUS );
                 break ;
            case ExpressionElement . MULTIPLE:
                 expression .add ( ExpressionOperator. OP_MULTIPLE );
                 break ;
            case ExpressionElement . DIVIDE:
                 expression .add ( ExpressionOperator. OP_DEVIDE );
                 break ;
            default :
                 try
                 {
                      ExpressionOperand operand = new ExpressionOperand (content ) ; // 构造时使用了parseDouble
                      expression .add ( operand) ;

                 } catch (Exception e)
                 {
                      return false ;
                 }

            }
            return true ;
      }

      @Override
      public String toString()
      {
            boolean firstAdd = true ;
            StringBuilder sb = new StringBuilder () ;
            for ( ExpressionElement e : expression )
            {
                 if ( ! firstAdd)
                 {
                      sb .append ( " ") ;
                 } else
                 {
                      firstAdd = false;
                 }
                 sb .append ( e. toString ());
            }
            return sb . toString() ;
      }

      @Override
      public Iterator < ExpressionElement> iterator()
      {
            return expression . iterator() ;
      }

      public void clear()
      {
            this .expression . clear() ;
      }

      // 获取表达式最终的运算的结果
      public abstract double getResultValue() throws Exception ;

}

class SuffixExpression extends Expression
{

      private double doPlus( ExpressionOperand a , ExpressionOperand b )
      {
            return a . getValue() + b .getValue () ;
      }

      private double doMinus( ExpressionOperand a , ExpressionOperand b )
      {
            return a . getValue() - b .getValue () ;
      }

      private double doMultiple( ExpressionOperand a , ExpressionOperand b )
      {
            return a . getValue() * b .getValue () ;
      }

      private double doDevide( ExpressionOperand a , ExpressionOperand b )
      {
            return a . getValue() / b .getValue () ;
      }

      // SuffixExpression 本身已经是一个后缀表达了。getResultValue计算出结果就OK了
      @Override
      public double getResultValue () throws Exception
      {
            SimpleStack <ExpressionOperand > scalc = new SimpleStack <>() ;

            for ( ExpressionElement e : expression )
            {

                 if ( e instanceof ExpressionOperand)
                 {
                      scalc .push (( ExpressionOperand) e) ;
                 } else if ( e instanceof ExpressionOperator )
                 {

                      ExpressionOperator operator = ( ExpressionOperator) e; // 获取这个运算符
                      ExpressionOperand opf = scalc .pop () ; // 弹出二元运算符的第二个操作数
                      ExpressionOperand ops = scalc .pop () ; // 弹出二元运算符的第一个操作数
                      ExpressionOperand temp = null ; // 存储临时运算结果

                      if ( opf == null || ops == null )
                            throw new Exception( "表达式不合法,不能完成计算" ) ;

                      if ( operator. equals (ExpressionOperator . OP_PLUS))
                      {
                            temp = new ExpressionOperand (doPlus ( ops, opf)) ;
                      } else if ( operator. equals (ExpressionOperator . OP_MINUS))
                      {
                            temp = new ExpressionOperand (doMinus ( ops, opf)) ;
                      } else if ( operator. equals (ExpressionOperator . OP_MULTIPLE))
                      {
                            temp = new ExpressionOperand (doMultiple ( ops, opf)) ;
                      } else if ( operator. equals (ExpressionOperator . OP_DEVIDE))
                      {
                            temp = new ExpressionOperand (doDevide ( ops, opf)) ;
                      }

                      scalc .push ( temp) ;

                 } // else if

            } // end foreach

            if ( scalc .size () != 1)
                 throw new Exception( "表达式不合法,不能完成计算" ) ; // 从 scalc栈中取出最后一个元素就是结果

            return scalc . pop() . getValue() ;
      }

}

class InfixExpression extends Expression
{
      public SuffixExpression toSuffixExpression()
      {
            SuffixExpression suffix = new SuffixExpression () ; // suffix是一个用来存储表达式元素的线性表对象,对应算法中的L
            SimpleStack <ExpressionElement > sop = new SimpleStack <>() ; // sop 栈

            // 遍历原始表达式中的每一个元素
            for ( ExpressionElement e : expression )
            {
                 if ( e instanceof ExpressionOperand) // 如果是操作数,则直接追加到后缀表达式suffix中
                 {
                      suffix .append ( e) ;
                 } else if ( e instanceof ExpressionDelimeter ) // 如果是分界符
                 {
                      if ( e. equals (ExpressionDelimeter . DM_LEFT_PARENTHESES )) // 是 左括号,则直接压栈
                      {
                            sop .push ( e) ;
                      } else if ( e. equals (ExpressionDelimeter . DM_RIGHT_PARENTHESES )) // 是 右括号,则从 sop中弹出与这个括号配对的中间的所有元素,
                      { // 并追加到后缀表达式中
                            while ( ! sop. isEmpty () && ! sop. peek (). equals (ExpressionDelimeter . DM_LEFT_PARENTHESES ))
                            {
                                 suffix .append ( sop. pop ()); // 将元素出栈,追加到 suffix 表中去
                            }

                            if ( ! sop. isEmpty ())
                            {
                                 sop .pop () ; // 将栈顶的( 出栈,丢弃。
                            }
                      }

                 } else if ( e instanceof ExpressionOperator ) // 如果是运算符
                 {

                      while ( ! sop. isEmpty () && sop . peek() instanceof ExpressionOperator
                                 && 0 >= (( ExpressionOperator) e ). compareTo ((ExpressionOperator ) sop . peek()))
                      {
                            suffix .append ( sop. pop ());
                      }
                      sop .push ( e) ;

                 }
            } // end of foreach

            // 将 sop栈中剩余的元素全部追加到suffix后
            while ( ! sop. isEmpty ())
            {
                 suffix .append ( sop. pop ());
            }

            return suffix ;
      }

      @Override
      public double getResultValue () throws Exception
      {
            return toSuffixExpression () .getResultValue () ;
      }

}

/**
 *
 * 因为Java集合框架中的Stack类是线程安全的, 但是这里不需要这种特性,为了提高效率,使用双端队列作为内部实现,自己封装成为一个栈数据结构。
 *
 * @see java.util.Stack <E>
 * @see java.util.Deque <E>
 * @see java.util.LinkedList <E>
 */
class SimpleStack < E>
{

      private final Deque< E > deque = new LinkedList < E> ();

      public E pop()
      {
            return deque . pop() ; // 双端队列的第一个元素,也就是栈的栈顶
      }

      public E peek()
      {
            return deque . peek() ;
      }

      public void push( E e )
      {
            deque .push ( e) ; // 压栈
      }

      public int size()
      {
            return deque . size() ;
      }

      public boolean isEmpty()
      {
            return 0 == deque .size () ;
      }

      @Override
      public String toString()
      {
            StringBuilder sb = new StringBuilder () ;
            sb .append ( "栈顶[") ;
            for ( E e : deque)
            {
                 sb .append ( e. toString () + "," ) ;
            }

            sb .append ( "]栈底") ;

            return sb. toString ();
      }

}
逆波兰式的计算(含有数学函数和浮点数) 这里的实现较为简单,也只能处理没有嵌套的表达式的情况。 相对完善的代码请见另一篇博客--完整的逆波兰式计算,修正了函数嵌套时出现的问题,调整的一定的 处理方式,使得代码更加简洁。不过大体思路与本代码基本相同。 //本代码提供的测试数据 //9.4+(3.27-1.05)*3.44+10/2.1+cos(0.52)+ln(3)/log(5)+3^2 #include&lt;stdio.h&... 阅读详情

相关推荐

波兰式、逆波兰式与表达式求值

波兰式、逆波兰式与表达式求值   《数据结构》中关于栈的解释经常会涉及到逆波兰式,波兰式,中缀式表达式的求值问题。但是,十分惭愧,整个大一阶段, 数据结构的课程没有上够5节,没有意识要学习,吃亏真的很大,只能现在恶补了。废话不说了,进入正题。 1. 中缀表达式 人类最熟悉的一种表达式1+2,(1+2)*3,3+4*2+4等等都是中缀表示法。对于人们来说,也是最直观的一种...

优快云 10万+

逆波兰表达式求值

逆波兰表达式又叫做后缀表达式。逆波兰表示法是波兰逻辑学家J・卢卡西维兹(J・ Lukasewicz)于1929年首先提出的一种表达式的表示方法 [1] 。后来,人们就把用这种表示法写出的表达式称作“逆波兰表达式”。逆波兰表达式把运算量写在前面,把算符写在后面。 逆波兰表达式就是后缀表达式,将运算量写在前面,运算符写在后面,如: 2, 1, +, 3, * 等价于 ((2 + 1) * 3...

凌桓丶的博客 2153

【每日蓝桥】6、一三年省赛Java组真题“逆波兰表达式”

你好呀,我是灰小猿,一个超会写bug的程序猿! 欢迎大家关注我的专栏“每日蓝桥”,该专栏的主要作用是和大家分享近几年蓝桥杯省赛及决赛等真题,解析其中存在的算法思想、数据结构等内容,帮助大家学习到更多的知识和技术! 标题:逆波兰表达式 正常的表达式称为中缀表达式,运算符在中间,主要是给人阅读的,机器求解并不方便 例如:3 + 5 *(2 + 6)- 1 而且常常需要使用括号来该表运算次序, 相反,如果使用逆波兰表达式(前缀表达式)表示,上面的算式则为: - + 3 * 5 + 2 6 1 不

累计发表博客60余万字,优快云 博客专家、Java 领域优质创作者、华为云享专家、阿里云专家博主。专注分享 Java 优质文章,毕设实战项目开发,全网粉丝10W+ 4810

逆波兰表达式

逆波兰 16:00~22:00花了好久好久学了一下这个东西 逆波兰真的好强 //逆波兰后缀表达式 利用堆栈实现 堆栈用数组实现 #include&lt;bits/stdc++.h&gt; using namespace std; #define stack astack char stack[500];//存符号 char out[500],in[500];//输出 输入 int dtop...

Peter_H 245

博途WinCC上位机避坑指南:从工控机选型到趋势图优化的完整流程

本文详细解析了博途WinCC上位机开发全流程,从工控机选型到趋势图优化的关键技巧。针对工业自动化场景,提供硬件配置指南、网络优化方案和性能提升策略,帮助工程师避开常见陷阱,实现稳定高效的HMI系统部署。特别强调PC Station与传统触摸屏的选型对比及SIMATIC NET配置优化。

pp12345的博客 279

【凌智视觉模块】rv1106 部署 pp-humseg 模型

人像分割技术实现摘要 人像分割是利用计算机视觉技术将图像/视频中的人物与背景分离的像素级处理技术。凌智视觉模块基于RV1106芯片开发,支持高效视觉模型部署。当前主流方法包括PP-HumanSeg、BiSeNet等深度学习模型,其中PP-HumanSegV2以96.63% mIoU精度和63FPS手机端速度成为SOTA方案。 实现流程包含: 初始化RKNN模型后端 图像预处理(尺寸调整、颜色空间转换) 执行模型推理 后处理(概率解码、动态阈值分割、形态学优化) 代码实现要点: 使用RKNPU2Backend

m0_50786932的博客 986

逆波兰表达式介绍及求值实现

1. 初始化两个栈:运算符栈s1和存储中间结果的栈s2 2. 从左至右扫描中缀表达式 3. 遇到操作数时,将其压s2 4. 遇到运算符时,比较其与s1栈顶运算符的优先级 1. 如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈 2. 否则,若优先级比栈顶运算符的高,也将运算符压入s1 3. 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4.1)与s1中新的栈顶运算符相比较 5. 遇到括号时: 1. 如果是左括号“(”,则直接压入s1 2. 如果是右括号“)”

wenwenaier的博客 6408

rosbag分析传感器数据的时间同步情况方法

ROS传感器时间同步分析流程:通过rosbaginfo初步检查bag文件信息,使用rqt_bag可视化时间线分布,采用rostopicecho或Python脚本提取各传感器时间戳并计算时间差统计值(均值/最大差/标准差),最后分析接收与采样时间延迟。量化评估同步精度,识别固定偏移或随机抖动问题,确保多传感器数据对齐,为后续融合处理提供可靠数据基础。

start_up_go的博客 202

逆波兰表达式算法 java_【算法】表达式求值--逆波兰算法介绍

packagecom. lulix86 .calc ;importjava. util .ArrayList ;importjava. util .Deque ;importjava. util .HashMap ;importjava. util .Iterator ;importjava. util .LinkedList ;importjava. util .List ;importjava...

weixin_42457435的博客 289

从 ABAP 视角看 JSON :7.40 版本带来的那些改变

摘要:ABAP 对 JSON 的原生支持 随着企业系统与外部 API 的交互增多,ABAP 从 NetWeaver 7.40 开始原生支持 JSON 格式。通过复用现有 XML 基础设施,ABAP 团队引入 JSON-XML 概念,使用 sXML Library 和 IF_SXML=>CO_XT_JSON 常量处理 JSON 数据。开发人员可通过 CALL TRANSFORMATION 语句实现 ABAP 数据与 JSON 的双向转换,大幅简化了与外部系统的数据交互。这种标准化的 JSON 处理方式提

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 180

YOLOv9改进策略目录 | 包含卷积、主干、检测头、注意力机制、Neck上百种创新机制

Hello,各位读者们好本专栏自开设两个月以来已经更新改进教程50余篇其中包含RepNCSPELAN4、主干、检测头、注意力机制、Neck多种结构上创新,也有损失函数和一些细节点上的创新。同时本人一些讲解视频和包含我所有创新的YOLOv9文档并不能在优快云上传(所有的创新点都经过我的测试是可用的,得到该文件之后大家可以随意组合使用),所以会建立群的形式在内上传我的文件和视频我也会在群内不定期和大家交流回答大家问题,同时定期会更新一些文章的创新点(经过我融合测试后的,先到先得)。

Snu77的博客 1万+

每日算法_表达式求值&逆波兰表达式求值

每日算法_表达式求值&逆波兰表达式求值 先来看一道力扣上比较简单的, 题目链接 : 150. 逆波兰表达式求值 根据 逆波兰表示法,求表达式的值。 有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。 说明: 整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。 输入:tokens = ["2","1","+","3","*"] 输出:9 解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3

Boring_Firecar的博客 2191

Qt5+Ros+GUI界面

在之前建立的工程的基础上添加一个gui界面。 在ros包中新建一个文件夹,名字叫gui 右击gui文件夹添加一个文件,选择Qt->Qt设计师界面类 选择MainWindow 选择下一步 最后在gui中生成三个文件 mainwindow.cpp、mainwindow.h、mainwindow.ui 但此时,这些新创建的文件和原本的ros工程还没建立任何关系,只是目录下多出了几个文件而已...

二毛的博客 8847

逆波兰式求值

根据 逆波兰表示法,求表达式的值。有效的运算符包括 + , - , * , /。每个运算对象可以是整数,也可以是另一个逆波兰表达式。说明:整数除法只保留整数部分。给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。示例 3:输入: [“10”, “6”, “9”, “3”, “+”, “-11”, " * ", “/”, " * ", “17”, “+”, “5”, “+”]输出: 22逆波兰表达式:是一种后缀表达式,所谓后缀就是指运算符写在后面。

m0_73494933的博客 819

YOLO26-seg分割优化:卷积魔改 | 动态蛇形卷积(Dynamic Snake Convolution),实现暴力涨点 | ICCV2023

​​​​​​摘要:本研究对Ultralytics YOLO26进行了全面分析,重点阐述了其关键架构改进及其在实时边缘目标检测中的性能基准测试。YOLO26于2025年9月发布,是YOLO系列最新、最先进的成员,专为在边缘及低功耗设备上实现高效、精确且易于部署的目标而构建。本文依次详述了YOLO26的架构创新,包括:移除了分布焦点损失(DFL);采用端到端的无NMS推理;集成了渐进损失(ProgLoss)与小目标感知标签分配(STAL);以及引入了用于稳定收敛的MuSGD优化器。

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 218

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

【数据结构】你知道波兰表达式和逆波兰表达式吗?我才知道原来栈在表达式求值中还能这样使用……

【数据结构】第三章——栈、队列与数组 详细介绍波兰表达式与逆波兰表达式以及通过栈实现相应表达式求值……

蒙奇D索大的博客 1791

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

逆波兰表达式求值(leetcode 150)

平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 )。该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * )。假设表达式长度为 n,那么操作数的个数为 ceil(n/2) 向上取整。逆波兰表达式的特点是:没有括号,运算符总是放在和它相关的操作数之后。因此,逆波兰表达式也称后缀表达式。通过观察逆波兰表达式的求值过程可以发现,整个过程就是操作数入栈出栈的过程。整个逆波兰表达式遍历完毕之后,栈内只有一个元素,该元素即为逆波兰表达式的值。

Doublelv 的博客专栏。 1148

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

leecode 150逆波兰表达式求值

leecode150.逆波兰表达式求值 题目 算法实现 将一个普通的中序表达式转换为逆波兰表达式的一般算法是: 首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为输入逆波兰式的栈S2(空栈),S1栈可先放入优先级最低的运算符#,注意,中缀式应以此最低优先级的运算符结束。可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤: (1)若取出的字符是操...

weixin_44178061的博客 291

栈的最后表演:逆波兰表达式求值

今天刷题遇到了逆波兰表达式,死亡的记忆突然开始攻击我,好嘛,既然根基不牢,那么就一次性给他搞明白了!

LEE180501的博客 490

JavaScript逆波兰表达式求值

逆波兰表达式简介 逆波兰表达式又叫做后缀表达式。逆波兰表示法是波兰逻辑学家J・卢卡西维兹(J・ Lukasiewicz)于1929年首先提出的一种表达式的表示方法 。后来,人们就把用这种表示法写出的表达式称作“逆波兰表达式”。逆波兰表达式把运算量写在前面,把算符写在后面。 逻辑提问式类似于算术表达式,对于检索而言,这种表达式并不是最优和最简洁的形式,需要进行必要的转换。1929年波兰的逻辑学家卢卡西维兹(Jan Lucasi

Twinkle_sone的博客 1388

150. 逆波兰表达式求值

题目描述 根据逆波兰表示法,求表达式的值。有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。 说明: 整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。 示例 示例 1: 输入: ["2", "1", "+", "3", "*"] 输出: 9 解释: ((...

路漫漫其修远兮 251

逆波兰表达式求值(栈)

逆波兰表达式求值 根据逆波兰表示法,求表达式的值。 有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。 说明: 整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。 示例 1: 输入: [“2”, “1”, “+”, “3”, “*”] 输出: 9 解释: ((2 + 1) * 3) = 9...

didi1663478999的博客 445
上一篇: 【编译原理】LL1文法语法分析器
下一篇: 取消Chrome黑暗模式
Estrellas_
博客等级 码龄9年 273粉丝 233原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值