计算器的实现

news2024/11/19 15:14:39

计算器的实现

在这里插入图片描述

计算器实现思路

  • 我们⽇常写的计算表达式都是中缀表达式,也就是运算符在中间,运算数在两边,但是直接读取⽆ 法⻢上进⾏运算因为⼀个计算表达式还涉及运算符优先级问题。如: 都⽆法运算,因为后⾯还有括号优先级更⾼。 1-2 * ( 3-4)+5 中遇到-和*

  • 所以其中⼀种实现思路是把中缀表达式转换为后缀表达式,也就是说分析计算表达式的优先级,将 运算符放到前⾯,运算符放到运算数的后⾯,然后我们依次读取后缀表达式,遇到运算符就可以进 ⾏运算了。后缀表达式也就做逆波兰表达式(ReversePolishNotation,RPN),这种表⽰法由波兰逻 辑学家J·卢卡西维兹于1929年提出,后来被⼴泛应⽤于计算机科学中。

后缀表达式进⾏运算

  • 后缀表达式因为已经确定好优先级,运算符⽅式⾮常简单,就是遇到运算符时,取前⾯的两个运算 符进⾏运算,因为经过中缀转后缀优先级已经确定好了
  • 建⽴⼀个栈存储运算数,读取后缀表达式,遇到运算数⼊栈,遇到运算符,出栈顶的两个数据进⾏ 运算,运算后将结果作为⼀个运算数⼊栈继续参与下⼀次的运算。读取表达式结束后,最后栈⾥⾯ 的值就是运算结果。

代码如下:

 class Solution {
 public:
 int evalRPN(const vector<string>& tokens) {
 stack<int> s;
 for (size_t i = 0; i < tokens.size(); ++i)
 {
 const string& str = tokens[i];
 // str为运算数
 
if (!("+" == str || "-" == str || "*" == str || "/" == str))
 {
 s.push(stoi(str));
 }
 else
 {
 // str为运算符
 
int right = s.top();
s.pop();
                int left = s.top();
                s.pop();
                switch (str[0])
                {
                case '+':
                    s.push(left + right);
                    break;
                case '-':
                    s.push(left - right);
                    break;
                case '*':
                    s.push(left * right);
                    break;
                case '/':
                    s.push(left / right);
                    break;
                }
            }
        }
        return s.top();
    }
 };

中缀表达式转后缀表达式

转换思路

  • 依次读取计算表达式中的值,遇到运算数直接输出。
  • 建⽴⼀个栈存储运算符,利⽤栈后进新出性质,遇到后⾯运算符,出栈⾥⾯存的前⾯运算符进⾏⽐ 较,确定优先级。
  • 遇到运算符,如果栈为空或者栈不为空且当前运算符⽐栈顶运算符优先级⾼,则当前运算符⼊栈。 因为如果栈⾥⾯存储的是前⼀个运算符,当前运算符⽐前⼀个优先级⾼,说明前⼀个不能运算,当 前运算符也不能运算,因为后⾯可能还有更⾼优先级的运算符。
  • 遇到运算符,如果栈不为为空且当前运算符⽐栈顶运算符优先级低或相等,说明栈顶的运算符可以 运算了,则输出栈顶运算符,当前运算符继续⾛前⾯遇到运算符的逻辑。
  • 如果遇到(),则把括号的计算表达式当成⼀个⼦表达式,跟上思路类似,进⾏递归处理⼦表达式, 处理后转换出的后缀表达式加在前⾯表达式的后⾯即可。
  • 计算表达式或者()中⼦表达式结束值,输出栈中所有运算符。

代码如下:

class Solution {
 public:

    //map<char, int> _operatorPrecedence = { { '+', 1 }, { '-', 1 }, { '*', 2 
}, { '/', 2 } };
    int operatorPrecedence(char ch)
    {
        struct opPD
        {
            char _op;
            int _pd;
        };
        static opPD arr[] = { {'+', 1},{'-', 1},{'*', 2},{'/', 2} };
        for (auto& e : arr)
        {
            if (e._op == ch)
            {
                return e._pd;
            }
        }
        assert(false);
        return -1;
    }
    void toRPN(const string& s, size_t& i, vector<string>& v)
    {
        stack<char> st;
        while (i < s.size())
        {
            if (isdigit(s[i]))
            {
                // 操作数输出
 
                string num;
                while (i < s.size() && isdigit(s[i]))
                {
                    num += s[i];
                    ++i;
                }
                v.push_back(num);
            }
            else
            {
                if (s[i] == '(')
                {
                    // 递归⽅式处理括号中的⼦表达式
 
                    ++i;
                    toRPN(s, i, v);
 }
                else if (s[i] == ')')
                {
                    ++i;
                    // 栈中的运算符全部输出
 
                    while (!st.empty())
                    {
                        v.push_back(string(1, st.top()));
                        st.pop();
                    }
                    // 结束递归
 
                    return;
                }
                else
                {
                    // 运算符
 
                    // 1、如果栈为空或者栈不为空且当前运算符⽐栈顶运算符优先级⾼,则当前运算符⼊栈
 
                    // 2、如果栈不为为空且⽐栈顶运算符优先级低或相等,说明栈顶的运算符可以运算了,
 
                    // 输出栈顶运算符,当前运算符继续⾛前⾯遇到运算符的逻辑
 
                    if (st.empty() || operatorPrecedence(s[i]) > 
operatorPrecedence(st.top()))
                    {
                        st.push(s[i]);
                        ++i;
                    }
                    else
                    {
                        v.push_back(string(1, st.top()));
                        st.pop();
                    }
                }
            }
        }
        // 栈中的运算符全部输出
 
        while (!st.empty())
        {
            v.push_back(string(1, st.top()));
            st.pop();
        }
    }
 };
 int main()
{
    size_t i = 0;
    vector<string> v;
    //string str = "1+2-3";
    string str = "1+2-(3*4+5)-7";
    Solution().toRPN(str, i, v);
    for (auto& e : v)
    {
        cout << e << " ";
    }
    cout << endl;
    return 0;
 }

计算器实现

  • 有了上⾯两个部分计算器OJ的⼤部分问题就解决了,但是这⾥还有⼀些问题需要处理。因为OJ中 给的中缀表达式是字符串,字符串中包含空格,需要去掉空格。

• 其次就是负数和减号,要进⾏区分,将所有的负数-x转换为0-x。

class Solution {
 public:
    //map<char, int> _operatorPrecedence = { { '+', 1 }, { '-', 1 }, { '*', 2 }, { '/', 2 } };
    int operatorPrecedence(char ch)
    {
        struct opPD
        {
            char _op;
            int _pd;
        };
        static opPD arr[] = { {'+', 1},{'-', 1},{'*', 2},{'/', 2} };
        for (auto& e : arr)
        {
            if (e._op == ch)
            {
                return e._pd;
            }
        }
  assert(false);
        return -1;
    }
    void toRPN(const string& s, size_t& i, vector<string>& v)
    {
        stack<char> st;
        while (i < s.size())
        {
            if (isdigit(s[i]))
            {
                // 运算数输出
 
                string num;
                while (i < s.size() && isdigit(s[i]))
                {
                    num += s[i];
                    ++i;
                }
                v.push_back(num);
            }
            else
            {
                if (s[i] == '(')
                {
                    // 递归⽅式处理括号中的⼦表达式
 
                    ++i;
                    toRPN(s, i, v);
                }
                else if (s[i] == ')')
                {
                    ++i;
                    // 栈中的运算符全部输出
 
                    while (!st.empty())
                    {
                        v.push_back(string(1, st.top()));
                        st.pop();
                    }
                    // 结束递归
 
                    return;
                }
                else
                {
                    // 运算符
// 1、如果栈为空或者栈不为空且当前运算符⽐栈顶运算符优先级⾼,则当前运算符⼊栈
 
                    // 2、如果栈不为为空且⽐栈顶运算符优先级低或相等,说明栈顶的运算符可以运算了,
 
                    // 输出栈顶运算符,当前运算符继续⾛前⾯遇到运算符的逻辑
 
                    if (st.empty() || operatorPrecedence(s[i]) > 
operatorPrecedence(st.top()))
                    {
                        st.push(s[i]);
                        ++i;
                    }
                    else
                    {
                        v.push_back(string(1, st.top()));
                        st.pop();
                    }
                }
            }
        }
        // 栈中的运算符全部输出
 
        while (!st.empty())
        {
            v.push_back(string(1, st.top()));
            st.pop();
        }
    }
    int evalRPN(const vector<string>& tokens) {
        stack<int> s;
        for (size_t i = 0; i < tokens.size(); ++i)
        {
            const string& str = tokens[i];
            // str为数字
 
            if (!("+" == str || "-" == str || "*" == str || "/" == str))
            {
                s.push(stoi(str));
            }
            else
            {
                // str为操作符
 
                int right = s.top();
                s.pop();
                int left = s.top();
                s.pop();
 switch (str[0])
                {
                case '+':
                    s.push(left + right);
                    break;
                case '-':
                    s.push(left - right);
                    break;
                case '*':
                    s.push(left * right);
                    break;
                case '/':
                    s.push(left / right);
                    break;
                }
            }
        }
        return s.top();
    }
    int calculate(string s)
    {
        // 1、去除所有空格,否则下⾯的⼀些逻辑没办法处理
 
        std::string news;
        news.reserve(s.size());
        for (auto ch : s)
        {
            if (ch != ' ')
                news += ch;
        }
        s.swap(news);
        news.clear();
        // 2、将所有的负数-x转换为0-x 
        for (size_t i = 0; i < s.size(); ++i)
        {
            if (s[i] == '-' && (i == 0 || (!isdigit(s[i - 1]) && s[i - 1] != 
')')))
                news += "0-";
            else
                news += s[i];
        }
        // 中缀表达式转成后缀表达式
 size_t i = 0;
 vector<string> v;
 toRPN(news, i, v);
 // 后缀表达式进⾏运算
 
return evalRPN(v);
 }
 };

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

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

相关文章

Python蓝桥杯刷题1

1.确定字符串是否包含唯一字符 题解&#xff1a;调用count函数计算每一个字符出现的次数&#xff0c;如果不等于1就输出no&#xff0c;并且结束循环&#xff0c;如果等于1就一直循环直到计算到最后一个字符&#xff0c;若最后一个字符也满足条件&#xff0c;则输出yes import…

《基于 PySpark 的电影推荐系统分析及问题解决》

以下是一篇关于上述代码的博客文章&#xff1a; 基于PySpark的电影推荐系统实现与分析 在当今数字化时代&#xff0c;个性化推荐系统在各个领域中都发挥着至关重要的作用&#xff0c;尤其是在娱乐行业&#xff0c;如电影推荐。本文将详细介绍如何使用PySpark构建一个简单的电…

每天五分钟深度学习pytorch:批归一化全连接网络完成手写字体识别

本文重点 前面我们学习了普通的全连接神经网络,后面我们学习了带有激活层的全连接神经网络,本文我们继续进一步升级,我们学习带有批归一化的全连接神经网络,批归一化可以加快神经网络的训练速度,减少过拟合,具体它的原理,大家可以看我们的《每天五分钟深度学习》专栏,…

JavaWeb后端开发知识储备1

目录 1.DTO/VO/PO 2.MVC架构/微服务架构 3.JWT令牌流程 4.ThreadLocal 5.接口路径/路径参数 6.自定义注解 1.DTO/VO/PO 1.1 DTO DTO 即 Data Transfer Object—— 数据传输对象&#xff0c;是用于传输数据的对象&#xff0c;通常在服务层与表现层之间传递数据&#xff…

什么是SMARC?模块电脑(核心板)规范标准简介三

1. 概念 SMARC&#xff08;Smart Mobility ARChitecture&#xff0c;智能移动架构&#xff09;是一种通用的小型计算机模块定义&#xff0c;基于ARM和X86技术的模块化计算机低功耗嵌入式架构平台&#xff0c;旨在满足低功耗、低成本和高性能的应用需求。这些模块通常使用与平板…

Filebeat升级秘籍:解锁日志收集新境界

文章目录 一、什么是filebeat二、Filebeat的工作原理2.1 filebeat的构成2.1.1 Prospector 组件2.1.2 Harvester 组件 2.2 filebeat如何保存文件的状态2.3 filebeat何如保证至少一次数据消费 三、Filebeat配置文件四、filebeat对比fluented五、Filebeat的部署安装5.1裸金属安装5…

C++小白实习日记——Day 4 将本地项目上传到gitee

生活就像一坨狗屎 我跑的代码老板说耗时太长了&#xff0c;不知道要怎么做才能耗时小一点 老板把我加到企业gitee里了&#xff0c;让我将代码上传到个人仓库&#xff1a; 新建一个文件夹当做库文件&#xff0c;点git bash here——> git init——>git config --global…

qiankun主应用(vue2+element-ui)子应用(vue3+element-plus)不同版本element框架css样式相互影响的问题

背景&#xff1a;qiankun微前端架构实现多应用集成 主应用框架&#xff1a;vue2 & element-ui 子应用框架&#xff1a;vue3 & element-plus >> 问题现象和分析 登录页面是主应用的&#xff0c;在登录之后才能打开子应用的菜单页面&#xff0c;即加载子应用。 首…

云渲染,解决houdini特效缓存太大上传太慢的问题

对于从事 Houdini 创作的艺术家和设计师们来说&#xff0c;使用云渲染的朋友&#xff0c;缓存太大导致云渲染上传慢一直是一个令人头疼的问题。然而&#xff0c;现在有了成都渲染 101 云渲染&#xff0c;这个难题迎刃而解。Houdini 以其强大的功能能够创建极为复杂和逼真的特效…

前端开发迈向全栈之路:规划与技能

一、前端开发与全栈开发的差异 前端开发主要负责构建和实现网页、Web 应用程序和移动应用的用户界面。其工作重点在于网页设计和布局&#xff0c;使用 HTML 和 CSS 技术定义页面的结构、样式和布局&#xff0c;同时运用前端框架和库如 React、Angular 或 Vue.js 等构建交互式和…

学习QT第二天

QT6示例运行 运行一个Widgets程序运行一个QT Quick示例 工作太忙了&#xff0c;难得抽空学点东西。-_-||| 博客中有错误的地方&#xff0c;请各位道友及时指正&#xff0c;感谢&#xff01; 运行一个Widgets程序 在QT Creator的欢迎界面中&#xff0c;点击左侧的示例&#xf…

删除课表中课程

文章目录 概要整体架构流程技术细节小结 概要 业务分析 删除课表中的课程有两种场景&#xff1a; 用户直接删除已失效的课程 用户退款后触发课表自动删除 技术细节 退款通知 其中用户退款与用户报名课程类似&#xff0c;都是基于MQ通知的方式。具体代码是在tj-trade模块的…

MySQL深度剖析-索引原理由浅入深

什么是索引&#xff1f; 官方上面说索引是帮助MySQL高效获取数据的数据结构&#xff0c;通俗点的说&#xff0c;数据库索引好比是一本书的目录&#xff0c;可以直接根据页码找到对应的内容&#xff0c;目的就是为了加快数据库的查询速度。 索引是对数据库表中一列或多列的值进…

MATLAB常见数学运算函数

MATLAB中含有许多有用的函数,可以随时调用。 a b s abs abs函数 a b s abs abs函数在MATLAB中可以求绝对值,也可以求复数的模长:c e i l ceil ceil函数 向正无穷四舍五入(如果有小数,就向正方向进一)f l o o r floor floor函数 向负无穷四舍五入(如果有小数,就向负方向…

如何使用本地大模型做数据分析

工具&#xff1a;interpreter --local 样本数据&#xff1a; 1、启动分析工具 2、显示数据文件内容 输入&#xff1a; 显示/Users/wxl/work/example_label.csv 输出&#xff1a;(每次输出的结果可能会不一样&#xff09; 3、相关性分析 输入&#xff1a; 分析客户类型与成…

操作系统不挂科】<创建线程(7)>单选多选简答题(带答案与解析)

前言 大家好吖&#xff0c;欢迎来到 YY 滴操作系统不挂科 系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 本博客主要内容&#xff0c;收纳了一部门基本的操作系统题目&#xff0c;供yy应对期中考试复习。大家可以参考 本章为选择题题库&#xff0c;试卷…

RAG(Retrieval-Augmented Generation)评测篇

一、为什么需要 对 RAG 进行评测&#xff1f; 在探索和优化 RAG&#xff08;检索增强生成器&#xff09;的过程中&#xff0c;如何有效评估其性能已经成为关键问题。 二、如何合成 RAG 测试集&#xff1f; 假设你已经成功构建了一个RAG 系统&#xff0c;并且现在想要评估它的…

宝塔 docker 部署onlyoffice 服务

1.宝塔安装docker,直接下载安装就行 2.docker拉取onlyoffice镜像 docker pull onlyoffice/documentserver:5.3.1.26 5.4或更高的版本已经解决了连接数限制方法的Bug 3.创建容器 docker run -d --name onlyoffice --restartalways -p 暴露端口号:80 onlyoffice/documentserv…

强制放大缩小(适用于所有ctrl-,ctrl+)

以下操作&#xff1a; 使用资源管理器打开启动文件夹&#xff1a; 按下 Win R 键打开“运行”对话框。输入 shell:startup&#xff0c;然后按下 Enter。这应该会打开启动文件夹。 手动定位启动文件夹&#xff1a; 打开资源管理器并导航到以下路径&#xff1a; C:\Users\admin…