2023-11-30 通过中缀表达式转换后缀表达式, 用C语言完成一个简单的计算器


点击 <C 语言编程核心突破> 快速C语言入门


通过中缀表达式转换后缀表达式, 用C语言完成一个简单的计算器

  • 前言
  • 一、中缀表达式和后缀表达式 (AI辅助)
  • 二、中缀转后缀规则及后缀运算规则 (AI辅助)
  • 总结


前言

要解决问题: 在练习用Qt完成一个简单的计算器时, 需要将一个文本计算式转换为C语言可使用的模式, 即后缀表达式, 规则还是挺繁复的.

想到的思路: 查文档, 了解中缀表达式转换为后缀表达式.

其它的补充: 需要用到栈, 这个基本功一定要扎实.


一、中缀表达式和后缀表达式 (AI辅助)

中缀表达式是指常见的数学表达式,如 2 + 3 * 4 - 5,其中运算符位于操作数之间。

中缀表达式通常需要通过加入括号来明确运算顺序。

后缀表达式(也称为逆波兰表达式)是一种不需要括号的表达式表示方式,其中运算符位于其相应的操作数之后。

例如,上述中缀表达式的后缀形式为 2 3 4 * + 5 -

在计算机科学中,后缀表达式比中缀表达式更容易被计算机算法处理和计算,因为它们没有嵌套括号的优先级问题。

因此,在编写计算机程序解析表达式时,通常会将中缀表达式转换为等效的后缀表达式以便更轻松地处理它们。

二、中缀转后缀规则及后缀运算规则 (AI辅助)

在后缀表达式中,运算符总是在它们所操作的两个操作数之后出现。

例如,对于后缀表达式 “3 4 + 5 *”,运算规则如下:

  1. 先将操作数 3 和 4 压入栈中。

  2. 遇到 “+” 运算符,弹出栈顶的 3 和 4 进行加法运算,结果为 7,再将结果 7 压入栈中。

  3. 遇到操作数 5,将其压入栈中。

  4. 遇到 “*” 运算符,弹出栈顶的 7 和 5 进行乘法运算,结果为 35,再将结果 35 压入栈中。

  5. 后缀表达式中的所有元素都被处理后,栈中只剩下一个元素 35,即为后缀表达式的计算结果。

具体规则见代码注释:

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 节点
struct node
{int flag;int value;struct node *next;
};// 栈
typedef struct node *Stack;// 压栈
void push(Stack *stack, int flag, int value)
{struct node *head = malloc(sizeof(struct node));head->flag = flag;head->value = value;head->next = *stack;*stack = head;
}// 出栈
int pop(Stack *stack)
{int rest = (*stack)->value;Stack temp = *stack;*stack = (*stack)->next;free(temp);return rest;
}// 判断栈是否为空
int empty(Stack *stack)
{return (*stack) == NULL;
}// 栈顶元素
int top(Stack *stack)
{int rest = (*stack)->value;return rest;
}// 栈顶flag
int topFlag(Stack *stack)
{return (*stack)->flag;
}// 释放栈
void freeStack(Stack *stack)
{while (*stack){struct node *head = *stack;*stack = (*stack)->next;free(head);}
}// 反转栈
void reverse(Stack *stack)
{if (!(*stack)){return;}Stack head;Stack tail = NULL;while (*stack){head = *stack;*stack = (*stack)->next;head->next = tail;tail = head;}*stack = head;
}// 返回优先级
int priority(char operate)
{switch (operate){case '+':case '-':return 1;case '*':case '/':case '%':return 2;default:return 0;}
}// 判断操作符
int isOperator(char operator)
{return (operator== '+' || operator== '-' || operator== '*' || operator=='/' ||operator== '%');
}// 判断左括号
int isLeftParenthesis(char operator)
{return operator== '(';
}// 判断右括号
int isRightParenthesis(char operator)
{return operator== ')';
}// 中缀表达式转后缀表达式的规则如下:
void convertToPostfix(char *input, Stack *postfix)
{Stack operateStack = NULL;static char number[32];// 1.从左到右遍历中缀表达式的每个元素。for (int i = 0; input[i] != '\0'; i++){// 2.如果当前元素是数字,则将其加入后缀表达式中。if (isdigit(input[i])){int num = 0;while (isdigit(input[i])){number[num++] = input[i++];}number[num] = '\0';i--;push(postfix, 1, atoi(number));}// 3.如果当前元素是操作符,则执行以下步骤://   a.如果该操作符是左括号“(”,则将其加入操作符栈中。else if (isLeftParenthesis(input[i])){push(&operateStack, 0, input[i]);}//   b.如果该操作符是右括号“)”,则将操作符栈中的操作符弹出并加入后缀表达式中,直到遇到左括号为止。else if (isRightParenthesis(input[i])){while (!isLeftParenthesis((char)top(&operateStack))){push(postfix, 0, pop(&operateStack));}pop(&operateStack);}else if (isOperator(input[i])){//   c.如果该操作符的优先级比操作符栈顶操作符的优先级低或相等//     则将操作符栈中的操作符弹出并加入后缀表达式中//     直到不存在比当前操作符优先级高的操作符为止。while (!empty(&operateStack) &&priority(input[i]) <= priority((char)top(&operateStack))){push(postfix, 0, pop(&operateStack));}//     将当前操作符加入操作符栈中。//   d.如果该操作符的优先级比操作符栈顶操作符的优先级高,则将当前操作符加入操作符栈中。push(&operateStack, 0, input[i]);}}// 4.当中缀表达式遍历完后,将操作符栈中的所有操作符依次弹出并加入后缀表达式中。while (!empty(&operateStack)){push(postfix, 0, pop(&operateStack));}freeStack(&operateStack);reverse(postfix);
}// 计算
int calculate(int operand1, int operand2, char operate)
{switch (operate){case '+':return operand1 + operand2;case '-':return operand1 - operand2;case '*':return operand1 * operand2;case '/':return operand1 / operand2;case '%':return operand1 % operand2;default:return 0;}
}// 计算后缀表达式的规则如下:
int evaluatePostfix(Stack *postfix)
{Stack stack = NULL;// 1.从左到右扫描后缀表达式。while (*postfix){// 2.遇到操作数时,将其压入操作数栈中。if ((*postfix)->flag){push(&stack, 1, pop(postfix));}// 3.遇到运算符时,弹出栈顶的两个操作数,执行该运算符的计算,并将结果压入操作数栈中。else if (!(*postfix)->flag){int operand2 = pop(&stack);int operand1 = pop(&stack);int result = calculate(operand1, operand2, (char)pop(postfix));push(&stack, 1, result);}}// 4.重复上述过程,直到后缀表达式中的所有元素都被处理。// 5.最后,操作数栈中只剩下一个数,即为后缀表达式的计算结果。int result = pop(&stack);freeStack(&stack);return result;
}// 打印后缀表达式栈
void printStack(Stack stack)
{while (stack){if (stack->flag){printf("%d ", stack->value);stack = stack->next;}else{printf("%c ", stack->value);stack = stack->next;}}printf("\n");
}// 测试
int main()
{char input[128];Stack postfix = NULL;printf("请输入含加减乘除、求模及括号的复杂算式:");scanf("%s", input);convertToPostfix(input, &postfix);printf("后缀表达式为:\n");printStack(postfix);int result = evaluatePostfix(&postfix);printf("计算结果为:%d\n", result);freeStack(&postfix);return 0;
}

总结

用C语言对字符串算式做语法分析并得出计算结果, 是很多教材的标准示例, 但并不简单, 属于数据结构和算法的一个类型, 考验栈结构和操作函数, 中缀转后缀算法, 后缀计算算法等, 慢慢体会.


点击 <C 语言编程核心突破> 快速C语言入门


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

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

相关文章

指数退避和抖动

目录 引入 OCC 添加退避机制 添加抖动机制 小结 引入 OCC 乐观并发控制&#xff08;Optimistic Concurrency Control&#xff0c;OCC&#xff09;是一种既能保证多个写入者安全地修改单个对象又能避免丢失写入的古老方法OCC具有三个优点&#xff1a;只要底层存储可用&#…

指针、数组与函数例题

1、简单数字显示 题目描述 本例要求实现对变量的直接访问和间接访问。输入任意两个整数&#xff0c;先用直接访问的方式输出这两个变量的值&#xff0c;再通过指针变量用间接访问的方式输出这两个变量的值。 输入要求 输入两个整数 输出要求 先用直接访问方式使出这两个整…

leetCode 131.分割回文串 + 回溯算法 + 图解 + 笔记

131. 分割回文串 - 力扣&#xff08;LeetCode&#xff09; 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。回文串 是正着读和反着读都一样的字符串 示例 1&#xff1a; 输入&#xff1a;s "aa…

从原理和公式出发:python实现One_Way_ANOVA

文章目录 目的&#xff1a;python实现one way ANOVA 单因素方差分析1. 代码流程2. python代码实现0 主要的函数1 加载数据2 查看数据统计结果3 数据处理及可视化4 方差分析4.1 模型拟合4.2 单因素方差分析 5 Post Hoc t-test组间比较分析6 根据定义自行分解计算对比调用函数的结…

pandas-profiling / ydata-profiling介绍与使用教程

文章目录 pandas-profilingydata-profilingydata-profiling实际应用iris鸢尾花数据集分析 pandas-profiling pandas_profiling 官网&#xff08;https://pypi.org/project/pandas-profiling/&#xff09;大概在23年4月前发出如下公告&#xff1a; Deprecated pandas-profilin…

积分表二(高等数学同济版中所有的积分公式)

文章目录 含有 x − a x a \sqrt{\pm \frac{x-a}{xa}} xax−a​ ​ 或者 ( x − a ) ( b − x ) \sqrt{(x-a)(b-x)} (x−a)(b−x) ​ 的积分含有三角函数函数的积分含有反三角函数的积分 (其中 a > 0 a>0 a>0)含有指数函数的积分含有对数函数的积分含有双曲函数的…

Object常用方法

toString()&#xff1a;返回对象的字符串表示形式。hasOwnProperty(propertyName)&#xff1a;检查对象是否有指定的属性。isPrototypeOf(object)&#xff1a;检查一个对象是否是另一个对象的原型。propertyIsEnumerable(propertyName)&#xff1a;检查对象的指定属性是否可以枚…

LeetCode 4 寻找两个正序数组的中位数

题目描述 给定两个大小分别为 m 和 n 的正序&#xff08;从小到大&#xff09;数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (mn)) 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,3], nums2 [2] 输出&#xff1a;2.0…

MediaPipe - 小记

文章目录 关于 MediaPipe 关于 MediaPipe Cross-platform, customizable ML solutions for live and streaming media. MediaPipe &#xff1a; 设备端机器学习框架 MediaPipe Studio : 提供标准化 task&#xff0c;你可以下载预训练模型来使用 MediaPipe Model Maker : 定制…

python中的字符串

字符串 字符串是编程语言中的一种基本数据类型&#xff0c;用于表示一串字符序列。在Python中&#xff0c;字符串是不可变的&#xff0c;也就是说一旦字符串被创建&#xff0c;就无法修改其中的字符。 Python中的字符串可以用单引号或双引号括起来&#xff0c;例如&#xff1…

构建SQL Server链接服务器:实现跨服务器数据访问及整合

点击上方蓝字关注我 在SQL Server数据库管理中&#xff0c;链接服务器是一项强大的功能&#xff0c;允许在一个SQL Server实例中访问另一个SQL Server实例的数据。这种功能为数据库管理员提供了灵活性&#xff0c;使其能够跨不同服务器进行数据交互&#xff0c;开辟了更多的应用…

RLHF:强化学习结合大预言模型的训练方式

RLHF (Reinforcement Learning from Human Feedback) 以强化学习方式依据人类反馈优化语言模型。 文章目录 一、简介二、一般的流程三、微调gpt介绍示例 参考文章 一、简介 强化学习从人类反馈中学习&#xff08;RLHF&#xff0c;Reinforcement Learning from Human Feedback&a…

【代码随想录】算法训练计划37

贪心 1、738. 单调递增的数字 题目&#xff1a; 输入: n 10 输出: 9 思路&#xff1a; func monotoneIncreasingDigits(n int) int {// 贪心&#xff0c;利用字符数组s : strconv.Itoa(n)ss : []byte(s)leng : len(ss)if leng < 1 {return n}for i:leng-1; i>0; i-- …

为什么不把所有的函数都定义成内联函数

内联是以代码膨胀为代价&#xff0c;仅仅省去了函数调用的开销&#xff0c;从而提高函数的执行效率。如果执行函数体内代码的时间相比于函数调用的开销较大&#xff0c;那么效率的收获会很少。另一方面&#xff0c;每一处内联函数的调用都要复制代码&#xff0c;将使程序的总代…

vscode集成git

1、首先电脑要安装git 打开git官网地址&#xff1a;Git进行下载&#xff0c;如下图界面&#xff1a; 如图片中描述&#xff1a;一般进入官网后会识别电脑对应系统&#xff08;识别出了我的电脑是Windows系统 。如果未识别到电脑系统&#xff0c;可在左侧选择自己电脑对应的系统…

主成分分析例题 (多元统计分析期末复习)

例一 给定X的协差阵&#xff0c;对其进行主成分分析, &#xff08;1&#xff09;求出每个主成分的贡献率&#xff1b; &#xff08;2&#xff09;求出每个原始变量的信息提取率&#xff1b; 解&#xff1a;对于主成分分析的题&#xff0c;一般来说&#xff0c;题目给定一个协方…

us提升到ns级精度settimeofday()到clock_settime()

精度 clock_settime()精度比settimeofday()高&#xff0c;受限制于参数的结构体&#xff0c;一个是ns级精度&#xff0c;另一个us级精度。具体两个函数原因参考如下。 int clock_settime(clockid_t clk_id, const struct timespec *tp); int settimeofday(const struct timeva…

[vue3] 使用 vite 创建vue3项目的详细流程

一、vite介绍 Vite&#xff08;法语意为 “快速的”&#xff0c;发音 /vit/&#xff0c;发音同 “veet”) 是一种新型前端构建工具&#xff0c;能够显著提升前端开发体验&#xff08;热更新、打包构建速度更快&#xff09;。 二、使用vite构建项目 【学习指南】学习新技能最…

Elasticsearch:使用 ILM 示例运行降采样 (downsampling)

如果你对降采样还不是很熟的话&#xff0c;请阅读之前的文章 “Elasticsearch&#xff1a;对时间序列数据流进行降采样&#xff08;downsampling)”。这是一个简化的示例&#xff0c;可让你快速了解降采样如何作为 ILM 策略的一部分来减少一组采样指标的存储大小。 该示例使用典…

2021年3月9日 Go生态洞察:Go开发者调研结果解读

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…