“栈”的典型应用—表达式求值(C语言实现)

“栈”的典型应用—表达式求值(C语言实现)表达式求值是程序设计语言编译中的一个基本问题。它的实现就是对“栈”的典型应用。本文针对表达式求值使用的是最简单直观的算法“算符优先法”。我们都知道算术四则运算的运算规则是:先乘除,后加减。从左到右计算先算括号内,再算括号外表达式组成任何一个表达式都有操作数、运算符和界定符组成。操作数即可以是常量,也可以是被说明为变量或常量的标识符。运算符可以分为算术运算,关系运算和

大家好,又见面了,我是你们的朋友全栈君。

表达式求值是程序设计语言编译中的一个基本问题。它的实现就是对“栈”的典型应用。本文针对表达式求值使用的是最简单直观的算法“算符优先法”。

我们都知道算术四则运算的运算规则是:

先乘除,后加减。

从左到右计算

先算括号内,再算括号外

表达式组成

任何一个表达式都有操作数、运算符和界定符组成。

操作数即可以是常量,也可以是被说明为变量或常量的标识符。

运算符可以分为算术运算,关系运算和逻辑运算符。

界定符有左右括号和结束符等。

本文为了方便演示只使用算术运算。

运算符优先级

对于连个相继出现的操作符θ1和θ2 有三种关系:大于、等于和小于。由此可以列出“+-*/”之间的优先级。如下表:

  + * / ( ) #
+ > > < < < > >
> > < < < > >
* > > > > < > >
/ > > > > < > >
( < < < < < =  
) > > > >   > >
# < < < < <   =

 

加减乘除优先性都低于“(”但是高于“)”,由运算从左到右可知,当θ12 ,令θ12

为了算法简洁,在表达式的左边和右边虚设一个“#”,这一对“#”表示一个表达式求值完成。

“(”=“)”当一对括号相遇时表示括号内已运算完成。

“)”和“(”、“#”和“(”、“(”和“#”无法相继出现如果出现则表达式出现语法错误。

为实现优先算法,可以使用两个工作栈,一个是OPTR,用于寄存运算符,一个是OPND,用于寄存运算数和运算结果。

算法基本思路。

首先置操作数栈为空栈,表达式起始符为“#”为栈底元素。

依次读入表达式中的每个字符,若是操作数则进OPND栈,若是运算符则和OPTR栈的栈顶运算符比较优先权作相应操作,直至整个表达式求值完毕(OPTR栈顶元素和当前读入的字符均为“#”)

代码实现:

首先先熟悉一下栈的相关操作:
#include "stdio.h"
#include "malloc.h"

#define TRUE        1
#define FALSE       0
#define OK          1
#define ERROR       0
#define INFEASIBLE -1
#define OVERFLOW    -2

typedef int  Status;
#define STACK_INIT_SIZE  100
#define STACKINCREMENT   10

typedef struct{
    SElemType *base;
    SElemType *top;
    int      stacksize;
}SqStack;
//构造一个空栈
Status InitStack(SqStack *S){
    S->base = (SElemType*)malloc(STACK_INIT_SIZE*sizeof(SElemType));

    if(!S->base)
        exit(OVERFLOW);
    S->top=S->base;
    S->stacksize=STACK_INIT_SIZE;
    return OK;
}
//判断是否为空栈
Status StackEmpty(SqStack S){
    if(S.top == S.base)
        return TRUE;
    else
        return FALSE;
}
//用e返回S的顶元素
Status GetTop(SqStack S, SElemType *e){
    if(S.top == S.base)
        return ERROR;
    *e = *(S.top-1);
    return OK;
}
//插入e为新的顶元素
Status Push(SqStack *S, SElemType e){
    if((S->top - S->base) >= S->stacksize){
        S->base = (
                    SElemType*)realloc(S->base,
                   (S->stacksize+STACKINCREMENT)*sizeof(SElemType)
                   );
        if(!S->base)
            exit(OVERFLOW);
        S->top = S->base +S->stacksize;
        S->stacksize += STACKINCREMENT;
    }
    *(S->top)=e;
    S->top++;
    return OK;
}
//删除S的顶元素,并用e返回其值
Status Pop(SqStack *S, SElemType *e){
    if(S->top == S->base)
        return ERROR;
    S->top--;
    *e = *(S->top);
    return OK;
}
//从栈底到栈顶依次对S的每个元素调用函数Visit(),一旦失败操作无效
Status ListTraverse(SqStack S,Status (*visit)(SElemType)){
    SElemType *p;
    p=S.base;
    for(p=S.base;p<S.top;p++)
        (*visit)(*p);

    return OK;
}
//输出元素e
Status output(SElemType e){
    printf("%d ",e);

    return OK;
}
实现表达式求值的代码:
/*计算整数表达式的值
 *表达式必须以#结束
 *表达式中可以出现多位数字,
 *表达式中可以出现空格
 *运算符包括+,-,*,/,(,)
 *运算结果可以是多位整数,并以整数的形式返回
 */
typedef int SElemType;      /*放入堆栈的元素的类型*/
#include <ctype.h>
#include "stack_s.c"
/*判断输入的某个字符是否是运算符
 *c表示输入的字符
 *op数组中存放系统能识别的运算符
 */
Status in(char c,char op[]){
    char *p;
    p=op;
    while(*p != '\0'){
        if(c == *p)
            return TRUE;
        p++;
    }
    return FALSE;
}
/*比较两个运算符的优先级
 *a,b中存放待比较的运算符
 *'>'表示a>b
 *'0'表示不可能出现的比较
 */
char Precede(char a, char b){
    int i,j;
    char pre[][7]={         
	/*运算符之间的优先级制作成一张表格*/
        {'>','>','<','<','<','>','>'},
        {'>','>','<','<','<','>','>'},
        {'>','>','>','>','<','>','>'},
        {'>','>','>','>','<','>','>'},
        {'<','<','<','<','<','=','0'},
        {'>','>','>','>','0','>','>'},
        {'<','<','<','<','<','0','='}};
    switch(a){
        case '+': i=0; break;
        case '-': i=1; break;
        case '*': i=2; break;
        case '/': i=3; break;
        case '(': i=4; break;
        case ')': i=5; break;
        case '#': i=6; break;
    }
    switch(b){
        case '+': j=0; break;
        case '-': j=1; break;
        case '*': j=2; break;
        case '/': j=3; break;
        case '(': j=4; break;
        case ')': j=5; break;
        case '#': j=6; break;
    }
    return pre[i][j];
}
/*进行实际的运算
 *a,b中分别以整数的形式存放两个待运算的操作数
 *theta中存放代表操作符的字符
 *结果以整数的形式返回
 */
int Operate(int a, char theta, int b){
    int i,j,result;
    i=a;
    j=b;

    switch(theta)   {
        case '+': result = i + j; break;
        case '-': result = i - j; break;
        case '*': result = i * j; break;
        case '/': result = i / j; break;
    }
    return result;
}
/*从输入缓冲区中获得下一个整数或运算符,并通过n带回到主调函数
 *返回值为1表示获得的是运算符
 *返回值为0表示获得的是整形操作数
 */
int getNext(int *n){
    char c;
    *n=0;
    while((c=getchar())==' ');  /*跳过一个或多个空格*/
    if(!isdigit(c)){            /*通过函数判断如果字符不是数字,那么只能是运算符*/
        *n=c;
        return 1;
    }
    do    {                         /*能执行到该条语句,说明字符是数字,此处用循环获得连续的数字*/
        *n=*n*10+(c-'0');       /*把连续的数字字符转换成相对应的整数*/
        c=getchar();
    }    while(isdigit(c));         /*如果下一个字符是数字,进入下一轮循环*/
    ungetc(c,stdin);            /*新读入的字符不是数字,可能是运算符,为了不影响下次读入,把该字符放回到输入缓冲区*/
    return 0;
}

int EvaluateExpression(){

    int n;
    int flag;
    int c;
    char x,theta;
    int a,b;

    char OP[]="+-*/()#";
    SqStack  OPTR;
    SqStack  OPND;

    InitStack(&OPTR);      
    Push(&OPTR,'#');
    InitStack(&OPND);
    flag=getNext(&c);

    GetTop(OPTR, &x);
    while(c!='#' || x != '#')
    {
        if(flag == 0)
	     {
                  Push(&OPND,c);
                  flag = getNext(&c);
             }        else
	{
            GetTop(OPTR, &x);
            switch(Precede(x, c))
	    {
                case '<'://栈顶元素优先级低                    
                    Push(&OPTR,c);
                    flag = getNext(&c);
                    break;
                case '='://脱括号并接受下一字符 
                    Pop(&OPTR,&x);
                    flag = getNext(&c);
                    break;
                case '>':// 退栈并将运算结果入栈                                       
                    Pop(&OPTR, &theta);
                    Pop(&OPND,&b);
                    Pop(&OPND,&a);
                    Push(&OPND, Operate(a, theta, b));
                    break;
            }
        }
        GetTop(OPTR, &x);
    }
    GetTop(OPND, &c);
    return c;
}

void main(){
    int c;
    printf("Please input one expression:");
    c=EvaluateExpression();
    printf("Result=%d\n",c);
    getch();
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/148439.html原文链接:https://javaforall.net

(0)
上一篇 2022年6月15日 下午10:16
下一篇 2022年6月15日 下午10:16


相关推荐

  • HTML学习14:框架布局

    HTML学习14:框架布局文章目录 1 框架页面的基本结构 2 水平布局 3 垂直布局 4 混合框架集 5 为框架添加内容 6 改进 使用 name 属性 7 给框架布局设置一些属性相对 div 布局 框架布局是一个很古老 但是易于学习和掌握的布局技术 当一个网站中包含很多页面 每个页面又包含一些同样的区域的时候 我们可以选择框架布局 在现代前端开发中 框架布局已经被淘汰 取而代之的是 DIV 结合 Ajax 页面无刷新的加载技术 因此 这里只对框架布局做简单的介绍 1 框架页面的基本结构 HTML5 不支持标签 2 水平布局 3 垂直布局 4

    2026年3月18日
    3
  • DDD 领域驱动设计落地实践:六步拆解 DDD

    DDD 领域驱动设计落地实践:六步拆解 DDD相信通过前面几篇文章的介绍 大家对于 DDD 的相关理论以及实践的套路有了一定的理解 但是理解 DDD 理论和实践手段是一回事 能不能把这些理论知识实际应用到我们实际工作中又是另外一回事 因此本文通过实际的业务分析把之前文章中涉及的理论和手段全部带着大家走一遍 我想通过这种方式 让大家实际的感受下 DDD 落地过程中会遇到哪些问题以及我们应该怎样去解决这些问题

    2026年3月16日
    2
  • Crontab定时任务配置

    Crontab定时任务配置一、开启crontab1.查看crontab是否启动sudolaunchctllist|grepcron2.检查需要的文件ls-al/etc/crontab3.如果crontab文件不存在则创建sudotouch/etc/crontab上述操作完成之后就可以正常使用crontab啦二、crontab服务开启、关闭1.开启su…

    2025年8月24日
    4
  • dsu之一

    dsu之一摆在桌上的文章有只有 11 页长 是关于所谓动态地软件更新 摘要关键字 1 引论 2 相关工作 3 结构 3 1 仓库管理器 3 2 映像管理器和内存管理器 3 3 引导装入器 4 模块化与依赖分析 5 动态代码更新 5 1 对函数的引用 5 2 活动的函数 5 3 调用栈上的函数 5 4 变量的更新 6 结论和当前状态引用从 3 开始 我认为有启发的思想就是对功能的抽象 他们根据不同阶段的功能抽象了 6 个 er ELFextractor nbsp RepositoryMa MemoryManage Linker ImageManager Bootl

    2026年3月19日
    1
  • 提交本地jar到Maven中央仓库(Windows&码云gitee托管代码)的那些小事

    提交本地jar到Maven中央仓库(Windows&码云gitee托管代码)的那些小事

    2022年4月2日
    54
  • 荔枝派linux安装python,GPIO · 荔枝派Zero指南 · 看云

    荔枝派linux安装python,GPIO · 荔枝派Zero指南 · 看云GPIO 操作本文讲述 linux 下 用户空间的 GPIO 操作的常规方法和 python 脚本操作的方法文件 IO 方式操作 GPIO 编号及复用功能在 Linux 中 GPIO 使用 0 MAX INT 之间的整数标识 对于 32 位 CPU 每组 GPIO32 个 引脚号就是按顺序排列 LicheePiZero 的所有 IO 的复用功能及 GPIO 标识号为 gt 总共 52 个 IO 所有 IO 上电默认状态为高阻态

    2026年3月17日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号