表达式求值;中缀表达式转后缀表达式;后缀表达式计算;栈;fgets与gets;C语言实现

表达式求值

http://www.nowcoder.com/questionTerminal/9566499a2e1546c0a257e885dfdbf30d

合法性检查比较糙;除法处理还不完善(除数为0或计算结果不是整数等情况)...
注意先写出步骤,完成每一部分先验证下;写了挺久的,简单题...

#include <stdio.h>

/*
C primer plus P331
fgets()可用来替代gets,fgets使用第二个参数限制读入的字符数来解决溢出问题(gets会有这个问题)
fgets的第二个参数指明了读入字符的最大数量,若为n,则读入n-1个字符(若读入n-1个字符且未遇到换行符,并使第n个字符为\0),或读到第一个换行符为止;
该函数会将换行符储存在字符串中(gets会丢弃换行符);
第3个参数指明要读入的文件,若从键盘读入,则参数为stdin
若读到文件结尾,则返回空指针(NULL)
与fputs配对使用(若要显示在计算机显示器上,则第二个参数为stdout),puts函数会在待输出字符串末尾添加一个换行符,而fputs不会。
*/

/*
一步步来,做完每一步先验证一下

参考数据结构与算法分析-C语言描述 P52-56

1. 检查合法性
2. 中缀表达式转为后缀表达式
3. 计算后缀表达式
后缀表达式没有必要知道任何优先规则

一个测试用例:
原数中缀表达式:1+2*3+(4*5+6)*7
转换后的后缀表达式:(加整数结束标志@的对比)
    代码实际输出:1@2@3@*+4@5@*6@+7@*+
    你期望的输出:123*+45*6+7*+
计算结果应为:189

!!还要考虑负数...
*/

int judge(char *s){
    char stack[101]="";
    int i;
    int top=-1;
    for(i=0;s[i]!='\0';i++){
        if(s[i]==' '){ // 跳过空格
            continue;
        }
        if(s[0]!=' ' && (s[0]<'0' || s[0]>'9') && (s[0]!='(') && (s[0]!='-')){ // 第一个非空格字符一定是数字或左括号或负号才合法
            return 0;
        }
        if((s[i]<'0' || s[i]>'9') && s[i]!='(' && s[i]!=')' &&
           (s[i]!='+') && s[i]!='-' && s[i]!='*' && s[i]!='/'){
            return 0; // 含不合法字符
        }
        if(s[i]=='('){ // 括号要成对
            top++;
            stack[top] = s[i];
        }
        if(s[i]==')'){
            if(top!=-1 && stack[top]=='('){
                top--; // 出栈左括号
            }else{
                return 0; // 括号不配对
            }
        }
    }
    if(top==-1){ // 不管有无入栈操作,遍历结束后,若合法总应是-1,若有括号且均成对,则栈为空
        return 1;
    }else{
        return 0;
    }
}

// 感觉空想有点绕,但是明确目的和返回值作用后就好多了
// 运算符在何时入栈:栈顶优先级更低时入栈,故返回0时需要入栈,返回1时需要出栈
int priority(char *stack, char cal, int top) // 判断cal与栈顶字符的优先级,栈顶优先级更低时返回0,优先级等于或高于时返回1
{
    switch(cal){
        case '+':
        case '-':
            return 1; // 栈顶无论是什么字符,都高于或等于+-,即没有比+-更低的优先级
        case '*':
        case '/':
            if(stack[top]=='+' || stack[top]=='-'){
                return 0; // 栈顶是+-时,优先级低于*、/
            }else{
                return 1;
            }
         // 如果是 ( ,则直接入栈
        case '(':
            if(stack[top]=='('){
                return 1;
            }else{
                return 0; // 除(,栈顶无论是其他什么字符,都低于该字符
            }
    }
}

int convert(char *s,char *stack) // 中缀表达式(s)转为后缀表达式(stack),返回存储后缀表达式的栈的栈顶
{
    int i;
    char calstack[101]=""; // 运算符栈
    int caltop=-1;
    int top=-1;
    for(i=0;s[i]!='\0';i++){
        if(s[i]>='0' && s[i]<='9'){ // 数字字符直接入栈,先不处理
            top++;
            stack[top]=s[i];
        }
        if(s[i]=='+' || s[i]=='-' ||  s[i]=='*' || s[i]=='/' || s[i]=='('){
            // 当遇到运算符时,则之前压入表达式栈的若为数字字符,则该整数已处理完毕,加上自定义结束符;
            // !!注意还要考虑运算符后和右括号前的整数,尤其是中缀表达式最后一个整数的情况
            if(stack[top]>='0' && stack[top]<='9'){
                top++;
                stack[top] = '@'; // 标志一个整数结束,便于后续计算
            }
            if(s[i]=='-'){ // 处理负数情况,若为负号,直接入栈
                if(s[i-1]=='+' || s[i-1]=='-' || s[i-1]=='*' || s[i-1]=='/' || s[i-1]=='('){
                    top++;
                    stack[top]='#'; // 标志负数(以免后面无法判断是减号还是负数)
                    continue;
                }
            }
            if(caltop==-1){ // 运算符栈为空,直接入栈
                caltop++;
                calstack[caltop]=s[i];
            }else{
                while(priority(calstack,s[i],caltop)==1){ // 出栈,栈内优先级相同或更高,需出栈直至遇到优先级更低的运算符(出栈放到后缀表达式中)
                    if(calstack[caltop] == '('){ // 特例,左括号只有遇到右括号时才出栈
                        break;
                    }
                    top++;
                    stack[top]=calstack[caltop];
                    caltop--;
                    if(caltop==-1){ // !!运算符栈为空时结束判断
                        break;
                    }
                }
                // 入栈,出栈完成后再将该运算符入栈(此时栈顶运算符优先级更低)
                caltop++;
                calstack[caltop] = s[i];
            }
        }
        if(s[i]==')'){
            if(stack[top]>='0' && stack[top]<='9'){ // 右括号前的整数
                top++;
                stack[top] = '@'; // 标志一个整数结束,便于后续计算
            }
            while(calstack[caltop]!='('){ // 出栈直至遇到对应的((前面已保证括号合法性)
                top++;
                stack[top] = calstack[caltop];
                caltop--;
            }
            caltop--;  // 左括号出栈,但不用保存,后缀表达式中不存在括号(没有优先级规则,只有四则运算符)
        }
    }
    // 中缀表达式最后一个整数(中缀表达式读取结束时,中缀最后一个字符应是右括号 或 数字字符,故在此处也要处理;右括号的情况在上面的遍历中已经处理了)
    if(stack[top]>='0' && stack[top]<='9'){
        top++;
        stack[top] = '@'; // 标志一个整数结束,便于后续计算
    }
    while(caltop!=-1){ // 输入读取完毕,将剩下的运算符栈元素弹出直至栈为空
        top++;
        stack[top]=calstack[caltop];
        caltop--;
    }
    top++;
    stack[top]='\0';
 //   printf("%s\n",stack);
    return (top-1);
}

int main()
{
    char s[101];
    while(gets(s)!=NULL){ // 使用fgets好像有点问题,先用gets
        char stack[250]=""; // 储存后缀表达式
        int nstack[101]={0}; // 计算后缀表达式用的栈,存放整数
        int ntop=-1;
        int top;
        int i,j,k;
        int a;
        int b;
        int res=0;
        if(judge(s)==0){ // 有不合法字符或表达式开头不合法或括号不合法
            printf("\n");
            break;
        }

        top = convert(s,stack); // 中缀表达式转为后缀表达式

        // 注意计算肯定是要从头开始遍历的呀!0~top(命名好像有点问题,先不管了...)
        // 计算后缀表达式:遇到一个数就将其推入整数栈中,遇到一个运算符就弹出两个数,并将计算结果入栈,后缀表达式栈为空时,整数栈顶为计算结果
        for(i=0;i<=top;i++){
       //     printf("stack[%d]=%c\n",top,stack[top]);
            if(stack[i]=='@'){ // @标志一个整数结束,即接下来是一个整数,将数字字符转换为整数并入栈,如:中缀为12,入栈后从顶到底为@21,故倒着先输出的是低位
                j=i;
                j--;
                ntop++; // 准备入栈,!!注意nstack[ntop]也要清0,因为出栈时只是单纯的更改栈顶下标,此处要从0开始计算整数值才可以 !!
                for(k=1,nstack[ntop]=0; j!=-1 && (stack[j]>='0' && stack[j]<='9'); j--,k*=10){
                    nstack[ntop] = nstack[ntop] + (stack[j] - '0') * k;
                }
                if(j!=-1 && stack[j]=='#'){ // 处理负数
                    nstack[ntop] = 0 - nstack[ntop]; // 如:0-1=-1
                }
  //              printf("j=%d,nstack[%d] = %d\n",j,ntop,nstack[ntop]);
            }
            if(stack[i]=='+' || stack[i]=='-' || stack[i]=='*' || stack[i]=='/'){
                b = nstack[ntop];
                ntop--;
                a = nstack[ntop];
                ntop--;
  //              printf("stack[%d]=%c,a=%d,b=%d\n",i,stack[i],a,b);
                switch(stack[i]){ // 注意break....
                    case '+':
                        res = a+b;
  //                      printf("a+b=%d\n",res);
                        break;
                    case '-':
                        res = a-b;
                        break;
                    case '*':
                        res = a*b;
  //                      printf("a*b=%d\n",res);
                        break;
                    case '/':
                        res = a/b; // 按整数处理,暂不考虑除不尽的情况
                        break;
                }
                ntop++;
                nstack[ntop] = res; // 计算结果入栈
  //              printf("nstack[%d] = %d\n",ntop,nstack[ntop]);
            }
        }
        printf("%d\n",nstack[ntop]);
    }

    return 0;
}
全部评论
nice
点赞 回复 分享
发布于 2021-05-12 17:29

相关推荐

我即大橘:耐泡王
点赞 评论 收藏
分享
11 3 评论
分享
牛客网
牛客企业服务