栈的应用:表达式求值 (自己写个小型计算器) 讲解+代码实现


对栈的一个应用:表达式求值 进行实现
这个表达式求值实现与知道中缀表达式求后缀表达式方法差不多
这是因为 后缀表达式运算符按计算顺序出现,也即知道后缀表达式后可以求出这个算式的值

运算优先级定义

在这里插入图片描述
你可以把 θ 1 \theta1 θ1看成栈的的元素, θ 2 \theta2 θ2看成式准备入站的元素(对应代码中为 c)
是不是很麻烦???你可以这么理解

  1. 对于’(‘,当其准备入栈时,他的优先级可以理解为最高,所以在栈顶的四则运算优先级都比他低
  2. 对于’(‘, 当其入了栈后,他的优先级变低,所以他之后的四则运算的优先级都比他高
  3. 对于’ )‘, 其优先级与入栈后的’(‘ 优先级相同,所以其为准备入栈时,比在栈顶的四则运算优先级低
  4. 对于四则运算,这四个运算符都是左结合性,所以对于四则运算,当准备入栈元素和栈顶元素的相同时,以栈顶的优先级大
  5. 我们定义’#‘ 为表达式的结束标式,这个符号的优先级是最低的

代码实现

定义操作数与操作符栈

为了实现简单,我都是用了字符型的Data成员的结构体,所以每个操作数仅仅为一位(当然你也可以实现多位数的运算,仅仅是把操作数栈单数实现)

#include"stdio.h"
#include"stdlib.h"

#define add 10

//建立栈  有操作数和操作符栈,为了方便统一设置成char类型成员的结构体  以顺序栈实现 为了程序简单,在malloc后统一没有增加失败检验
//注意我为了方便才讲数据栈和操作符栈统一写成有char域的结构体, 你可以讲数据栈写成有int域的结构体
typedef struct SNode {
    char* Data; /* 存储元素的数组 */
    int Top;      /* 栈顶指针 */
    int MaxSize;       /* 堆栈最大容量 */
}* PNode;//运算符  操作数
对栈的相关操作

void InitStack(PNode stack) {
    stack->MaxSize = 5; //开始的栈大小为5  之后不够再加
    stack->Top = 0;
    stack->Data = (char*)malloc(sizeof(char) * stack->MaxSize);
}

void Push(PNode stack, char e) {
    if (stack->Top == stack->MaxSize + 1) {
        stack->Data = (char*)realloc(stack, sizeof(char)*(stack->MaxSize + add));
        stack->MaxSize = stack->MaxSize + add;
    }
    stack->Data[stack->Top++] = e;
}

void Pop(PNode stack, char* e) {
    if (stack->Top != 0)
        *e = stack->Data[--stack->Top];
}

char GetTop(PNode stack) {
    return stack->Data[stack->Top - 1];
}
表达式求值相关函数实现
定义运算符的优先级

注意我只实现了 + − ∗ / + - * / +/相关的运算符的优先级,如果你需要实现其他的,你要考虑该操作符与’(’, ‘)’, '#和’ + − ∗ / + - * / +/等的相关关系,还要考虑这个运算符和结合性(比如指数运算位右结合性)


/*栈的应用:表达式求值:*/
//假设仅有 + - * / 四个运算 和( ) 对应ASCII 43 45 42 47 40 41  # 35 代表起始符和终止符
//若想增加指数运算可以定义优先级 但要注意:指数为右结合

char Precede(PNode optr, char c) { //比较栈顶op与c的优先级
    char op = GetTop(optr);
    if (op == c && c == '(') return '<'; // 处理((情况
    else if (op == '(' && c == ')')  return '=';
    else if (op == '(') return '<';
    else if (c == '(')   return '<';
    else if (c == '#')   return '>';
    else if (c == ')')  return '>';
    else if (op == '#') return '<';
    else if (c == op && (c == 43 || c == 45 || c == 42 || c == 47))
        return '>';   //+ - * / 为左结合性
    else if ((op == 42 || op == 47) && (c == 43 || c == 45))
        return '>';
    else if (((op == 43 || op == 45) && (c == 43 || c == 45)) || ((op == 42 || op == 47) && (c == 42 || c == 47)))
        return '>'; //同一级运算符 从左到右结合
    else if (((op == 43 || op == 45) && (c == 42 || c == 47)))
        return '<'; //c的优先级大
}
实现计算函数
char Operate(char num1, char x, char num2) { //进行运算 在此时已近转化成了int值对应的char
    int n1 = num1 - 48;
    int n2 = num2 - 48;
    printf("operate %d %c %d\n", n1, x, n2);
    switch (x){
        case'+': return n1 + n2 + 48;
        case'-': return n1 - n2 + 48;
        case'*': return n1 * n2 + 48;
        case'/': return n1 / n2 + 48;
    }
}
实现输入数值函数

注意 “#” 代表输入表达式结束

int EvaluateExpression() {
    PNode optr = (PNode)malloc(sizeof(struct SNode));
    PNode opnd = (PNode)malloc(sizeof(struct SNode));;
    InitStack(optr);    InitStack(opnd);    
    Push(optr, '#');    printf("%c", GetTop(optr));   char c = getchar();
    
    char x; //保存弹出来的操作符
    char num1, num2; //保存弹出来的数字
    while (c != '#' || GetTop(optr) != '#') {
        //printf("%c\n", c);
        if (c > 47 && c < 58) { // 输入的位操作数
            Push(opnd, c);  c = getchar();
        }
        else {
            switch (Precede(optr, c)) {
                case'<': Push(optr, c); c = getchar(); break;
                case'=': Pop(optr, &x); c = getchar(); break;
                case'>': Pop(optr, &x); Pop(opnd, &num2); Pop(opnd, &num1);
                    Push(opnd, Operate(num1, x, num2)); break;
            }
        }
    }
    int num = GetTop(opnd) - 48; //转化成int
    return num; 
}
int main() {
    int a = EvaluateExpression();
    printf("结果是: %d", a);
}


结果

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值