c语言栈实现表达式求值

算术四则运算规则

  1. 先乘除,后加减
  2. 从左算到右
  3. 先括号内后括号外

表达式组成

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

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

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

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

若把运算符和界定符统称为算符,他们构成的集合命名为OP.则任意两个相继出现的算符a和b之间的有限关系至多是下面的三种之一:

  1. a < b   a的优先权高于b
  2. a < b   a的优先权低于b
  3. a = b   a的优先权等于b

OP的优先权关系如下表(0表示不存在)

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

加减乘除优先性都低于“(”,但是高于“)”,为了算法简洁,在表达式的左边和右边虚设一个“#”, 这一对“#”表示一个表达式求值完成 “(”=“)”表示当一对括号相遇时表示括号内已运算完成。 “)”和“(”、“#”和“(”、“(”和“#”无法相继出现如果出现则表达式出现语法错误。 为实现优先算法,可以使用两个工作栈,一个是OPTR,用于寄存运算符,一个是OPND,用于寄存运算数和运算结果。

##算法基本思路 1.首先置操作数栈为空栈,表达式起始符为“#”为栈底元素。 2.依次读入表达式中的每个字符,若是操作数则进OPND栈,若是运算符则和OPTR栈的栈顶运算符比较优先权作相应操作: 1. 栈顶元素优先权低:运算符入栈,并重新输入 2. 优先权一样:运算符弹栈,并重新输入 3. 栈顶元素优先权高:弹栈,计算,结果入栈 直至整个表达式求值完毕(OPTR栈顶元素和当前读入的字符均为“#”)

##代码实现 参考严蔚敏老师的《数据结构》(c语言版)的实现思路,代码由自己完成
#include"int_sp_stack.c"
#include <ctype.h>
/* 存储运算符和界限符 */
char OP[7] = {'+', '-', '*', '/', '(', ')', '#'};
/* OP的优先级表 */
char pre[][7] ={
                {'>', '>', '<', '<', '<', '>', '>'},
                {'>', '>', '<', '<', '<', '>', '>'},
                {'>', '>', '>', '>', '<', '>', '>'},
                {'>', '>', '>', '>', '<', '>', '>'},
                {'<', '<', '<', '<', '<', '=', '0'},
                {'>', '>', '>', '>', '0', '>', '>'},
                {'<', '<', '<', '<', '<', '0', '='}
               };


/* 输入函数
 * 若输入运算数(可以是多位数),存到指针n中,并返回true
 * 若输入运算符,存到指针c中,并返回false
*/
int get_input(int *n)
{
    *n = 0;
    char ch = getchar();
    /* 消除\n */
    getchar();
    if(!isdigit(ch))
    {
        *n = ch;
        return true;
    }
    do
    {
        *n = (*n) * 10 + (ch - '0');
        ch = getchar();
        /* 消除\n */
        getchar();
    }
    while(ch != ' ');
    return false;
}






/* 判断输入运算符和栈顶运算符的优先级
 * 若栈顶运算符的优先级高返回'>'
 * 若栈顶运算符的优先级低返回'<'
 * 若栈顶运算符和输入运算符的优先级相等返回'='
 * 否则返回'0'
*/

char precmp(char s_top, char ch)
{
    int i, j;
    switch(s_top)
    {
        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;
        default:i =0;break;
    }
    switch(ch)
    {
        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;
        default:j =0;break;
    }
    return pre[i][j];
}


/* 根据两个栈的弹栈内容计算 */
int compute(int a, char theta, int b)
{
    int result;
    switch(theta)
    {
        case '+':result = a + b;break;
        case '-':result = a - b;break;
        case '*':result = a * b;break;
        case '/':result = a / b;break;
    }
    return result;

}


/* 算数表达式求值的算符优先算法 
 * 设OPTR和OPND分别为运算符栈和运算数栈
*/

int evaluate_expression()
{
    /* 输入元素 */
    int *elem = (int *)malloc(sizeof(*elem));
    /* 输入元素类型
     * type = true表示输入为运算符
     * type = false表示输入为运算数
    */
    int type;
    /* 出栈元素 */
    int *x = (int *)malloc(sizeof(*x));
    /* 运算出栈元素 */
    int *theta = (int *)malloc(sizeof(*theta));
    int *a = (int *)malloc(sizeof(*a)); 
    int *b = (int *)malloc(sizeof(*b));
    /* 操作符栈 */
    int_sp_stack OPTR = (int_sp_stack)malloc(sizeof(int_stack));
    /* 操作数栈 */
    int_sp_stack OPND = (int_sp_stack)malloc(sizeof(int_stack));
    OPTR = stack_init(OPTR);
    OPND = stack_init(OPND);
    push(OPTR, '#');
    type = get_input(elem) ;
    *x = '#';
    while(*elem != '#' || *x != '#')
    {
        /* 输入不是运算符 */
        if(!type)
        {
            push(OPND, *elem);
            type = get_input(elem);
        }
        else
        {
            get_top(OPTR, x);
            switch(precmp(*x, *elem))
            {
                case '<':
                push(OPTR, *elem);
                type = get_input(elem);
                get_top(OPTR, x);
                break;
                case '=':
                pop(OPTR, x);
                get_top(OPTR, x);
                type = get_input(elem);
                break;
                case '>':
                pop(OPTR, theta);
                pop(OPND, b);
                pop(OPND, a);
                push(OPND, compute(*a, *theta, *b));
                get_top(OPTR,x);
                break;
            }
        }

    }
    get_top(OPND, x);
    stack_destroy(OPTR);
    stack_destroy(OPND);
    return *x;
}
int main()
{
    int x = evaluate_expression();
    printf("x = %d", x);
    return 0;
}

注意:对于getchar()函数来说,是从缓存流中获取一个字符并返回的.例如连续调用两次getchar()函数

char a = getcahr();
char b = getchar();

当执行第一个getchar()函数时,输入x并以回车键结束输入,这时当程序调用第二个getchar()函数时,回直接从缓存那取得换行符’\n’返回,也就是b=’\n’,而不会终止程序,等待从键盘再输入一个字符.想要从键盘输入一个字符赋给b,应先消除缓存中’\n’的影响,较简单的方法是在两个getchar()函数之间再调用一次getchar()函数.

上述程序,采用的是之前顺序栈的实现接口,程序正确执行的输入格式:例如计算(52 - 3 + 1) * 10

(
5
2
"空格"
-
3
"空格"
-
1
"空格"
)
*
1
0
"空格"
#
"回车"

结果截图:
这里写图片描述

改进输入:把int get_input(int n)函数改成下面的形式后,程序运行时输入格式为:例如计算(52 - 3 + 1) 10

int get_input(int *n)
{
    *n = 0;
    char ch = getchar();
    /* 消除\n */
    //getchar();
    if(!isdigit(ch))
    {
        *n = ch;
        getchar();
        return true;
    }
    do
    {
        *n = (*n) * 10 + (ch - '0');
        ch = getchar();
        /* 消除\n */
        //getchar();
    }
    while(ch != '\n');
    return false;
}
(
52
-
3
-
1
)
*
10
#
"回车"

结果截图:

这里写图片描述

  • 12
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值