数据结构实训:表达式求值器(非常详细)

表达式求值器

问题描述:

设计一个表达式求值器,能够解析和计算由数字、运算符和括号组成的算术表达式。要求实现基本的四则运算,如加、减、乘、除并处理运算符的优先级和括号

设计要点:

1. 使用栈作为数据结构来处理运算符和操作数的优先级

2. 实现表达式的解析算法,将输入的字符串转换为内部表示形式,如逆波兰表示法。

3. 设计求值算法,根据运算符的优先级和操作数的顺序进行计算。

完整代码(超详细)

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>#define MAX 100typedef struct {int top;char items[MAX];
} Stack;void initStack(Stack* s) {s->top = -1;
}int isEmpty(Stack* s) {return (s->top == -1);
}int isFull(Stack* s) {return (s->top == MAX - 1);
}void push(Stack* s, char value) {if (!isFull(s)) {s->items[++(s->top)] = value;}else {printf("栈满了\n");}
}char pop(Stack* s) {if (!isEmpty(s)) {return s->items[(s->top)--];}else {printf("栈为空\n");return 0;}
}char peek(Stack* s) {if (!isEmpty(s)) {return s->items[s->top];}else {return '\0';}
}int precedence(char op) {switch (op) {case '+':case '-':return 1;case '*':case '/':return 2;default:return 0;}
}int isOperator(char ch) {return (ch == '+' || ch == '-' || ch == '*' || ch == '/');
}void infixToPostfix(char* infix, char* postfix) {Stack s;initStack(&s);int k = 0;for (int i = 0; infix[i] != '\0'; i++) {if (isdigit(infix[i])) {while (isdigit(infix[i])) {postfix[k++] = infix[i++];}postfix[k++] = ' ';i--;}else if (infix[i] == '(') {push(&s, infix[i]);}else if (infix[i] == ')') {while (!isEmpty(&s) && peek(&s) != '(') {postfix[k++] = pop(&s);postfix[k++] = ' ';}pop(&s); }else if (isOperator(infix[i])) {while (!isEmpty(&s) && precedence(peek(&s)) >= precedence(infix[i])) {postfix[k++] = pop(&s);postfix[k++] = ' ';}push(&s, infix[i]);}}while (!isEmpty(&s)) {postfix[k++] = pop(&s);postfix[k++] = ' ';}postfix[k - 1] = '\0'; 
}int evaluatePostfix(char* postfix) {Stack s;initStack(&s);int i = 0;while (postfix[i] != '\0') {if (isdigit(postfix[i])) {int num = 0;while (isdigit(postfix[i])) {num = num * 10 + (postfix[i++] - '0');}push(&s, num);}else if (isOperator(postfix[i])) {int val2 = pop(&s);int val1 = pop(&s);switch (postfix[i]) {case '+': push(&s, val1 + val2); break;case '-': push(&s, val1 - val2); break;case '*': push(&s, val1 * val2); break;case '/': push(&s, val1 / val2); break;}i++;}else {i++;}}return pop(&s);
}int main() {char infix[MAX], postfix[MAX];printf("Enter an infix expression: ");fgets(infix, MAX, stdin);infix[strcspn(infix, "\n")] = '\0'; infixToPostfix(infix, postfix);printf("Postfix expression: %s\n", postfix);int result = evaluatePostfix(postfix);printf("Result: %d\n", result);return 0;
}

代码解释(主要看这个) 

 1.栈的实现

 typedef struct {

int top;

char items[MAX];

} Stack;

这里定义了一个结构体 Stack,它包含一个整数 top 用于指示栈顶位置,以及一个字符数组 items 作为栈的存储空间

  • initStack: 初始化栈,将 top 设置为 -1 表示栈为空。
  • isEmpty: 检查栈是否为空。
  • isFull: 检查栈是否已满。
  • push: 入栈操作,如果栈未满,将元素压入栈顶。
  • pop: 出栈操作,如果栈非空,弹出并返回栈顶元素。
  • peek: 查看栈顶元素,但不移除。

 2. 运算符优先级和判断

int precedence(char op) {
    switch (op) {
        case '+':
        case '-':
            return 1;
        case '*':
        case '/':
            return 2;
        default:
            return 0;
    }
}

int isOperator(char ch) {
    return (ch == '+' || ch == '-' || ch == '*' || ch == '/');
}

  • precedence: 返回运算符的优先级,+ 和 - 优先级较低(返回1),* 和 / 优先级较高(返回2),其他情况返回0。
  • isOperator: 判断字符是否为运算符。

3. 中缀转后缀表达式

void infixToPostfix(char* infix, char* postfix) {
    Stack s;
    initStack(&s);
    int k = 0;

    for (int i = 0; infix[i] != '\0'; i++) {
        if (isdigit(infix[i])) {
            while (isdigit(infix[i])) {
                postfix[k++] = infix[i++];
            }
            postfix[k++] = ' '; // 添加空格作为后缀表达式元素的分隔符
            i--; // 退回到数字的最后一个字符
        } else if (infix[i] == '(') {
            push(&s, infix[i]);
        } else if (infix[i] == ')') {
            while (!isEmpty(&s) && peek(&s) != '(') {
                postfix[k++] = pop(&s);
                postfix[k++] = ' ';
            }
            pop(&s); // 弹出 '('
        } else if (isOperator(infix[i])) {
            while (!isEmpty(&s) && precedence(peek(&s)) >= precedence(infix[i])) {
                postfix[k++] = pop(&s);
                postfix[k++] = ' ';
            }
            push(&s, infix[i]);
        }
    }

    // 将栈中剩余的运算符弹出并添加到后缀表达式中
    while (!isEmpty(&s)) {
        postfix[k++] = pop(&s);
        postfix[k++] = ' ';
    }
    postfix[k-1] = '\0'; // 移除最后一个空格,并添加字符串结束符
}

  • infixToPostfix: 将中缀表达式转换为后缀表达式。遍历中缀表达式,根据操作数、运算符和括号进行处理,并使用栈来管理运算符的顺序。

4. 后缀表达式求值

int evaluatePostfix(char* postfix) {
    Stack s;
    initStack(&s);
    int i = 0;
    while (postfix[i] != '\0') {
        if (isdigit(postfix[i])) {
            int num = 0;
            while (isdigit(postfix[i])) {

//这段代码用于解析并提取后缀表达式(即逆波兰表达式)中的数字
                num = num * 10 + (postfix[i++] - '0');

            }
            push(&s, num);
        } else if (isOperator(postfix[i])) {
            int val2 = pop(&s);
            int val1 = pop(&s);
            switch (postfix[i]) {
                case '+': push(&s, val1 + val2); break;
                case '-': push(&s, val1 - val2); break;
                case '*': push(&s, val1 * val2); break;
                case '/': push(&s, val1 / val2); break;
            }
            i++;
        } else {
            i++; // 跳过空格
        }
    }
    return pop(&s); // 返回最终的计算结果
}

num = num * 10 + (postfix[i++] - '0');详细解释
  1. isdigit(postfix[i]):

    • isdigit 是一个标准库函数,用于检查给定字符是否为数字字符('0' 到 '9')。如果 postfix[i] 是数字字符,则返回非零值,否则返回零。
    • postfix[i] 是当前正在处理的字符。
  2. num = num * 10 + (postfix[i++] - '0');:

    • num 是用来存储最终解析出的整数的变量。
    • postfix[i++] - '0'
      • postfix[i] 是当前的数字字符。
      • 减去字符 '0' 将字符转换为对应的整数值。例如,字符 '7' 减去 '0' 的 ASCII 值等于整数 7。
      • i++ 是后置递增操作,表示在使用 postfix[i] 之后,再将 i 增加 1,以便指向下一个字符。
    • num = num * 10 + ...
      • 每次循环中,将新的数字字符与之前解析的部分组合起来。
      • 例如,如果之前 num 是 12,现在读取到一个新的数字字符 '3',那么计算 num = 12 * 10 + 3 结果为 123。

  • evaluatePostfix: 对后缀表达式进行求值。遍历后缀表达式,遇到操作数则压入栈中,遇到运算符则弹出栈顶的两个操作数进行运算,并将结果压回栈中,直到表达式结束。

 5. 主函数

int main() {
    char infix[MAX], postfix[MAX];
    printf("Enter an infix expression: ");
    fgets(infix, MAX, stdin);
    infix[strcspn(infix, "\n")] = '\0'; // 去除末尾的换行符

    infixToPostfix(infix, postfix);
    printf("Postfix expression: %s\n", postfix);

    int result = evaluatePostfix(postfix);
    printf("Result: %d\n", result);

    return 0;
}

  • 主函数负责接收用户输入的中缀表达式,调用 infixToPostfix 转换为后缀表达式,然后调用 evaluatePostfix 计算结果并输出。

这些代码组合在一起实现了一个基本的表达式求值器,仅可以处理加减乘除四则运算和括号。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/36035.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ElementUI组件

目录 1、安装ElementUI 2、在main.js文件中加入 3、使用组件 终端运行&#xff1a; Element&#xff0c;一套为开发者、设计师和产品经理准备的基于Vue2.0的桌面端组件库. 1、安装ElementUI 控制台输入 npm i element-ui -S 2、在main.js文件中加入 import ElementUI from…

老司机开发技巧,如何扩展三方包功能

前言 最近碰上有个业务&#xff0c;查询的sql如下&#xff1a; sql 复制代码 select * from table where (sku_id,batch_no) in ((#{skuId},#{batchNo}),...); 本来也没什么&#xff0c;很简单常见的一种sql。 问题是我们使用的是mybatis-plus&#xff0c;然后写的时候有没…

【智能制造-5】数采和电机

既然可以采集PLC的数据&#xff0c;为什么要采集电机的数据&#xff1f; 采集PLC&#xff08;可编程逻辑控制器&#xff09;的数据和采集电机的数据是两个不同的概念和目的。 PLC是用于控制和监控工业自动化过程的设备&#xff0c;它可以接收传感器的输入信号并根据预设的逻辑…

多线程软件不响应处理

多线程的问题,基本上由于写法不规范造成的问题,从而影响软件正常运行,或时不时出现软件不响应,但是其它CPU,内存保存不变的情况. 出现这样的情况,多半是软件运行时死锁或多个线程相互等待,从而引起的软件未响应的情况发生. 解决办法: 1.while,do while循环增加延时时间Sleep…

重庆交通大学24计算机考研数据速览,专硕第二年招生,复试线321分!

重庆交通大学&#xff08;Chongqing Jiaotong University&#xff0c;CQJTU&#xff09;&#xff0c;是由重庆市人民政府和中华人民共和国交通运输部共建的一所交通特色、以工为主的多科性大学&#xff0c;入选“中西部高校基础能力建设工程”、“卓越工程师教育培养计划”、国…

企业级堡垒机JumpServer

文章目录 JumpServer是什么生产应用场景 Docker安装JumpServer1.Docker安装2.MySQL服务安装3.Redis服务安装4.key生成5.JumpServer安装6.登录验证 系统设置邮箱服务器用户和用户组创建系统审计员资产管理用户创建资产节点资产授权查看用户的资产监控仪表盘 命令过滤器创建命令过…

Model3C芯片方案--86彩屏中控面板Modbus协议说明

一、概述 Model3C芯片是一款基于RISC-V的高性能、国产自主、工业级高清显示与智能控制MCU&#xff0c;配备强大的2D图形加速处理器、PNG/JPEG解码引擎&#xff0c;并支持工业宽温。基于Model3C芯片的86彩屏中控面板&#xff0c;通过集成Modbus协议&#xff0c;实现了与多种控制…

前端存储都有哪些

cookie 、sessionStorage、localStorange、http缓存 、indexDB cookie 由服务器设置&#xff0c;在客户端存储&#xff0c;然后每次发起同源请求时&#xff0c;发送给服务器端。cookie最多能存储4K数据&#xff0c;它的生存时间由expires属性指定&#xff0c;并且cookie只能被…

涨点超强!图像特征提取最新方法!性能效率快到飞起

在图像处理领域&#xff0c;有一个非常关键的步骤&#xff1a;图像特征提取。它能给我们提供一种高效、准确且灵活的方式来描述和分析图像内容。 通过降低图像数据的维度&#xff0c;去除冗余和噪声信息&#xff0c;图像特征提取不但简化了后续处理过程&#xff0c;还能提高算…

ffmpeg使用mjpeg把yuvj420p编码为jpg图像

version #define LIBAVUTIL_VERSION_MAJOR 58 #define LIBAVUTIL_VERSION_MINOR 12 #define LIBAVUTIL_VERSION_MICRO 100 note 1. 通过*.jpg推测时&#xff0c;out_fmt为image2&#xff0c;打开*.jpg文件时&#xff0c;in_fmt为image2 但是out_fmt为image2时&#xff…

web项目打包成可以离线跑的exe软件

目录 引言打开PyCharm安装依赖创建 Web 应用运行应用程序打包成可执行文件结语注意事项 引言 在开发桌面应用程序时&#xff0c;我们经常需要将网页集成到应用程序中。Python 提供了多种方法来实现这一目标&#xff0c;其中 pywebview 是一个轻量级的库&#xff0c;它允许我们…

滑动窗口算法——部分OJ题详解

目录 关于滑动窗口 部分OJ题详解 209.长度最小的子数组 3.无重复字符的最长字串 1004.最大连续1的个数Ⅲ 1658.将x减到0的最小操作数 904.水果成篮 438.找到字符串中所有字母异位词 30.串联所有单词的子串 76.最小覆盖子串 关于滑动窗口 其实滑动窗口也是通过双指针…

存储引擎MyISAM和InnoDB

目录 一、存储引擎概述 1.存储引擎概念 2.存储引擎分类 3.选择存储引擎的原则 二、InnoDB 存储引擎 三、MyISAM 存储引擎 四、实验操作 一、存储引擎概述 1.存储引擎概念 数据库存储引擎是数据库底层软件组件&#xff0c;数据库管理系统使用数据库引擎进行创建&#x…

Spring Boot中如何处理异步任务

Spring Boot中如何处理异步任务 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将探讨在Spring Boot应用中如何处理异步任务&#xff0c;以提升系统的性…

Nuxt 的异步数据处理(八)

Nuxt.js 扩展了 Vue.js&#xff0c;增加了一个叫 asyncData 的方法&#xff0c;使得我们可以在设置组件的数据之前能异步获取或处理数据。 asyncData 方法 Nuxt.js 提供了几种不同的方法来使用 asyncData 方法&#xff0c;你可以选择自己熟悉的一种来用&#xff1a; 返回一个…

CMS垃圾回收过程中重新标记阶段为什么不能清理浮动垃圾

因为在并发标记时&#xff0c;因为是 GC 和用户线程是并发执行的&#xff0c;可能导致一部分已经标记为 从 GC Roots 不可达 的对象&#xff0c;若该对象在用户线程的修改下又可达了&#xff0c;Remark 的作用就是将这部分对象又标记为 可达对象&#xff08;漏标&#xff09;。…

Double 4 VR虚拟情景智能互动系统在小语种专业课堂上的应用

随着科技的进步&#xff0c;越来越多的教育机构开始尝试使用虚拟现实技术来提高教学效果。Double 4 VR虚拟情景智能互动系统就是这样一款能够为小语种专业课堂带来革新性体验的教学工具。 一、模拟真实环境&#xff0c;增强学习体验 系统通过高度仿真的虚拟环境&#xff0c;为学…

Git(涵盖GitHub\Gitee码云\GitLab)

Git(涵盖GitHub\Gitee码云\GitLab) 文章目录 Git(涵盖GitHub\Gitee码云\GitLab)课程介绍Git概述官网介绍版本控制介绍两种版本控制工具集中式版本控制工具分布式版本控制工具 Git工作机制代码托管中心 Git安装和客户端的使用Git常用命令设置用户签名初始化本地库查看本地库状态…

C++——string类用法指南

一、前言 在C语言中&#xff0c;字符串是以\0结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列的库函数&#xff0c;但是这些库函数与字符串是分离的&#xff0c;不太符合OOP的思想&#xff0c;而且底层空间需要用户自己管理&#xff0c;稍…

性能飞跃:深度优化IntelliJ IDEA的实战指南

性能飞跃&#xff1a;深度优化IntelliJ IDEA的实战指南 IntelliJ IDEA&#xff0c;作为Java开发者的首选IDE&#xff0c;以其强大的功能和智能化的代码辅助而闻名。然而&#xff0c;随着项目规模的增长&#xff0c;IDE的性能可能会成为瓶颈。本文将提供一份详尽的优化指南&…