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

    点击查看项目源码

  • 相关阅读:
    PHP的ip2long和long2ip函数的实现原理
    PHP在浏览器上跟踪调试的方法以及使用ChromePhp、FirePHP的简单介绍
    PHP静态延迟绑定简单示例
    [deviceone开发]-do_Socket组件示例
    [deviceone开发]-大家比较关注的应用内部升级
    [deviceone开发]-do_Viewshower的动画效果示例
    [deviceone开发]-企业OA项目开源分享
    [deviceone开发]-do_RichLabel的简单示例
    [deviceone开发]-直播APP心形点赞动画示例
    [deviceone开发]-纳豆项目源码开源
  • 原文地址:https://www.cnblogs.com/gylic/p/14165635.html
Copyright © 2011-2022 走看看