zoukankan      html  css  js  c++  java
  • 用栈求简单算术表达式的值

    用C#写了一个简易计算器,核心思路如下。查看项目源码请点击文末链接。

    问题描述

    这里限定的简单算术表达式求值问题是:用户输入一个包含“+”、“-”、“*”、“/”、正整数和圆括号的合法数学表达式,计算该表达式的运算结果。

    数据组织

    数学表达式exp采用字符数组表示,其中只含有“+”、”-“、“* ”、“/"、正整数和圆括号,为了方便,假设该表达式都是合法的数学表达式,例如,exp= "1+2*(4+12)”1;在设计相关算法中用到两个栈、一个运算符栈和一个运算数栈,均采用顺序栈存储结构,这两个栈的类型如下:

    struct OpType//运算符栈类型
    {
    public char[] data;//存放运算符
    public int top;//栈顶指针
    }
    
    struct ValueType//运算数栈类型
    {
    public double[] data;//存放运算符
    public int top;//栈顶指针
    }
    

    设计相关算法

    在算术表达式中,运算符位于两个操作数中间的表达式称为中缀表达式,例如,1+2*3就是一个中缀表达式。中缀表达式是最常用的一种表达式方式。对中缀表达式的运算一般遵循"先乘除,后加减,从左到右计算,先括号内,后括号外”的规则。因此,中缀表达式不仅要依赖运算符优先级,而且还要处理括号。

    所谓后缀表达式,就是运算符在操作数的后面,如1+2* 3的后缀表达式为123* +。在后缀表达式中已考虑了运算符的优先级,没有括号,只有操作数和运算符。

    对后缀表达式求值过程是:从左到右读人后缀表达式,若读入的是一个操作数,就将它入数值栈,若读入的是一个运算符op,就从数值栈中连续出栈两个元素(两个操作数),假设为x和y,计算x op y的值,并将计算结果入数值栈;对整个后缀表达式读入结束时,栈顶元素就是计算结果。

    表达式的求值过程是:先将算术表达式转换成后缀表达式,然后对该后缀表达式求值。

    假设用exp存放算术表达式,用字符数组postexp存放后缀表达式。设计如下求表达式值的类ExpressClass如下(后面求表达式值的方法均包含在该类中):

    class ExpressClass
    {
        const int MaxSize=100;
        pubic string exp;//存放中缀表达式
        public char[] postexp;//存放后缀表达式
        public int num;//postexp中的字符个数
        public OpType op = new OpType();//运算符栈
        public ValueType st = new ValueType();//运算数栈
        public expressClass()//构造函数,用于栈等的初始化
        {
          postexp = new char[MaxSize];
          pnum = 0;
          op.data = new char[MaxSize];
          op.top=-1;
          st.data= new double[MaxSize];
          st.top=-1;  
        }//表达式求值算法
    }
    

    将算术表达式转换成后缀表达式postexp的过程是:对于数字符,将其直接放到postexp中; 对于‘(’,将其直接进栈:对于‘)’,退栈运算符并将其放到postexp中,直到遇到‘(’。但不将‘(’放到postexp中。对于运算符(op_2),将其和栈顶运算符(op_1)的优先级进行比较,只有当(op_2)的优先级高于(op_1)的优先级时,才将(op_2)直接进栈,否则将栈中的‘(’,(如果有的话),以前的优先级等于或大于(op_2)的运算符均退栈并放到postexp中,再将(op_2)进栈。其描述如下:

    while (若exp未读完)
    {
      从exp读取字符ch;
      ch为数字:将后续的所有数字均依次存放到postexp中,并以字符'#'标志数值申结束;
      ch为左括号'(':将'('进栈;
      ch为右括号')':将op栈中'('以前的运算符依次出栈井存放到postxp中,再将'('退栈;
      若ch的优先级高于栈顶运算符优先级,则将ch进栈;否则退栈并存入postexp中,再将ch进栈;
    }  
      若字符串exp扫描完毕,则退栈op中的所有运算符并存放到postexp中。
    

    在简单算术表达式中,只有'*'和'/'运算符的优先级高于'+'和'-'运算符的优先级。 所以,上述过程可进一.步改为:

    while (若exp未读完)
    {
      从exp读取字符ch;
      ch为数字:将后续的所有数字均依次存放到postexp中,并以字符'#'标志数值申结束;
      ch为左括号'(':将'('进栈;
      ch为右括号')':将op栈中'('以前的运算符依次出栈并存放到postxp中,再将'('退栈;
      ch为'+'或'-':将op栈中'('以前的运算符依次出栈并存放到postxp中,再将'+'或'-'进栈;
      ch为'*'或'/':将op栈中'('以前的'*'或'/'运算符出栈并存放到postxp中,再将'*'或'/'进栈;
    }  
      若字符串exp扫描完毕,则退栈op中的所有运算符并存放到postexp中。
    

    表达式(56-20)/(4+2)转换成后缀表达式的过程见下表

    op栈 postexp 说明
    ( 遇到ch为'(',将此括号进栈op
    ( 56# 遇到ch为数字,将56存入数组exp中,并插入一个字符‘#’
    (- 56# 遇到ch为‘-’,由于op中‘(’以前没有字符,则直接将ch进栈op中
    (- 56#20# 遇到ch为数字,将20#存入数组exp中
    56#20#- 遇到ch为')',则将栈op中‘(’以前的字符依次删除并存入数组exp中,然后将‘(’删除
    / 56#20#- 遇到ch为'/',将ch进栈op中
    /( 56#20#- 遇到ch为‘(’,将此括号进栈op中
    /( 56#20#-4# 遇到ch为数字,将4#存入数组exp中
    /(+ 56#20#-4# 遇到ch为‘+’,由于op中‘(’以前没有字符,则直接将ch进栈op中
    /(+ 56#20#-4#2# 遇到ch为数字,将2#存入数组exp中
    / 56#20#-4#2#+ 遇到ch为‘)’,则将栈op中‘(’以前的字符依次删除存入数组exp中,然后将‘(’删除
    56#20#-4#2#+/ str扫描完毕,则将栈op中的所有运算符依次弹出并存入数组exp中,然后再将ch存入数组exp中,得到后缀表达式

    根据以上原理得到的Trans()算法如下:

    public void Trans()//将算术表达式exp转换成后缀表达式postexp
    {
    int i=0,j=0;//i j分别作为exp和postexp的下标
    char ch;
    while(i<exp.Length)//表达式未扫描完时循环
    {
     ch=exp[i];
     if(ch=='(')//判定为左括号
      { op.top++;
        op.data[op.top]=ch;
      }
      else if(ch==')')//判定为右括号
      {
       while(op.top!=-1&&op.data[op.top]!='(')
       {//将栈中'('前面的运算符退栈并存放到postexp中
        postexp[j++] = op.data[op.top];
        op.top--;
       }
       op.top--;//将'('退栈
      }
      else if(ch=='+'||ch=='-')//判断为加减号
    {
    while(op.top!=-1&&op.data[op.top]!='(')
    {//将栈中'('前面的运算符退栈并存放到postexp中
     postexp[j++] = op.data[op.top];
     op.top--;
    }
    op.top++;op.data[op.top]=ch;//将'+'或'-'进栈
    }
     else if(ch=='*'||ch=='/')//判断为乘除号
    {
    while(op.top!=-1&&op.data[op.top]!='('&&(op.data[op.top]=='*'||op.data[op.top]=='/'))
    {//将栈中'('前面的运算符退栈并存放到postexp中
     postexp[j++] = op.data[op.top];
     op.top--;
    }
    op.top++;op.data[op.top]=ch;//将'*'或'/'进栈
    }
    else//处理数字字符
    {
    while(ch>='0'&&ch<='9')//判断为数字
    {
    postexp[j++]=ch;
    i++;
    if(i<exp.Length) ch=exp[i];
    else break;
    }
    i--;
    postexp[j++]='#';//用#来标识一个数值串结束
    
    }
    i++;//继续处理其他字符
    
    }
    while(op.top!=-1)//退栈所有运算符并放到postexp中
    {
    postexp[j++]=op.data[op.top];
    op.top--;
    }
    pnum=j;
    
    }
    
    
    
    

    在后缀表达式求值算法中哟啊用到一个数值栈st。后缀表达式求值过程如下:

    while (若exp未读完)
    {
       从postexp读取字符ch;
       ch为'+':从栈st中出栈两个数值a和b,计算c = a+b;将c进栈;
       ch为'-':从栈st中出栈两个数值a和b,计算c = b-a;将c进栈;
       ch为'*':从栈st中出栈两个数值a和b,计算c = b*a;将c进栈;
       ch为'/':从栈st中出栈两个数值a和b,若a不为零,计算c = b/a;将c进栈;
       ch为数字字符:将连续的数字串转换成数值d,将d进栈;
    
    }
    

    后缀表达式” 56#20#-4#2#+/ “的求值见下表

    st栈 说明
    56 遇到56#,将56进栈
    56,20 遇到20#,将20进栈
    36 遇到‘-’,出栈两次,将56-20=36进栈
    36,4 遇到4#,将4进栈
    36,4,2 遇到2#,将2进栈
    36,6 遇到‘+’,出栈两次,将4+2=6进栈
    6 遇到‘/’,出栈两次,将36/6=6进栈
    postexp扫描完毕,算法结束,栈顶数值6即为所求

    根据上述计算原理得到的算法如下:

    public bool GetValue(ref double v)//计算后缀表达式postexp的值
    {
    double a,b,c,d;
    int i=0;
    char ch;
    while (i< pnum)//postexp字符串未扫描完时循环
    {
       ch= postexp[i];
       switch (ch)
      {
     case'+':               //判定为'+号
       a=st. data[st. top];  //退栈取数值a
       st. top--;
       b=st. data[st. top];  //退栈取数值b
       st. top--;
       c=a+b;                 //计算c
       st. top++;
       st. data[st. top]=c;//将计算结果进栈
       break;
     case'-'://判定为-号
       a=st. data[st. top];
       st.top -- ;//退栈取数值a
       b=st. data[st. top];
       st.top--;//退栈取数值b
       c=b- a;//计算c
       st. top++;
       st. data[st. top]=c;//将计算结果进栈
       break;
     case'*'://判定为'*'号
       a=st. data[st. top];
       st.top--;//退栈取数值a
       b= st. data[st. top];
       st.top-- ;//退栈取数值b
       c=a* b;//计算c
       st. top++;
       st. data[st. top]=c;//将计算结果进栈
       break;
     case /'://判定为/号
       a= st. data[st. top];st.top--;//退栈取数值a
       b=st. data[st. top];st.top-- ;//退栈取数值b
       if (a!=0)
       {
        c=b/ a;//计算c
        st. top++;
        st. data[st. top]=c;//将计算结果进栈
       }
       else return false;//除零错误,返回false
       break;
     default://处理数字字符
       d=0;//将连续的数字符转换成数值存放到d中
    
     while(ch>='0'&&ch<='9')
     {
       d= 10*d+ (ch- '0');
       i++ ;
       ch= postexp[ i];
     }
      st. top++ ;
      st. data[st. top]= d;
      break;
    }
    i++ ;//继续处理其他字符
    
    }
    v= st. data[st. top];
    return true;
        
    }
    

    点击查看项目源码

  • 相关阅读:
    vue自定义指令
    ZOJ Problem Set–2104 Let the Balloon Rise
    ZOJ Problem Set 3202 Secondprice Auction
    ZOJ Problem Set–1879 Jolly Jumpers
    ZOJ Problem Set–2405 Specialized FourDigit Numbers
    ZOJ Problem Set–1874 Primary Arithmetic
    ZOJ Problem Set–1970 All in All
    ZOJ Problem Set–1828 Fibonacci Numbers
    要怎么样调整状态呢
    ZOJ Problem Set–1951 Goldbach's Conjecture
  • 原文地址:https://www.cnblogs.com/gylic/p/14165635.html
Copyright © 2011-2022 走看看