前言
基础运算器,没有括号,只有加减乘除,运算数字不一定是只有一位的0~9,可能是多位。在提交了7遍之后,终于通过了。。。(大哭)
提示:本题思路及其简单直接,关键就是一些编程技巧,用得好可以简化代码
一、题目是什么?
给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。整数除法仅保留整数部分。
示例 1:
输入:s = “3+2*2”
输出:7
示例 2:
输入:s = " 3/2 "
输出:1
示例 3:
输入:s = " 3+5 / 2 "
输出:5
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/basic-calculator-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
注:虽然示例给的是个位数,但是题目要求是对于十进制数都可以的,要考虑如何把字符串转化为数字。
二、解答
1.我的解答
分析:
先乘除,后加减。运算符号和数字分别压栈
乘除:
遍历一遍字符串:1、遇到space,直接跳过 2、遇到数字,判断它到底有几位,利用ASCLL将字符串转化为long int 整型(之前用过int 整型,但是在测试中出现了溢出现象) 3、遇到加减,符号压栈 3、遇到*\,将这个符号前后两个数运算,将计算结果代替原来的top()。
加减:乘除中,我是在字符串从前往后这么遍历的,这就导致一个问题,之后进行加减运算的时候,还要把栈中元素的顺序reverse。
所以我在想,从后往前,会不会节省内存,这样就不用在reverse栈了。
class Solution {
public:
//第一遍先算乘除,第二遍算加减
//
int calculate(string s) {
stack<long int> symbol;//储存运算符号
stack<int> num;
stack<long int> symbol1;//储存运算符号
stack<int> num1;
int n=s.size();
long int temp=0;//溢出
long int help=0;
int i=0,j=-1;
//先乘除
while(i<n){
if(s[i]==32) i++;
else if(s[i]>='0'&&s[i]<='9') {
if(j==i-1&&j!=-1){
temp=num.top();
num.pop();
temp=temp*10+s[i]-'0';
num.push(temp);
j=i;
i++;//考虑一下十位数
}
else{
num.push(s[i]-'0');
j=i;
i++;
}
}
else if(s[i]==43){//+
symbol.push(1);
i++;
}
else if(s[i]==45){//-
symbol.push(-1);
i++;
}
else if(s[i]==42){//*
temp=num.top();
num.pop();
j=1;
while(s[i+j]==32) j++;
i=i+j;//开始下一个数
help=0;
while(s[i]>='0'&&s[i]<='9'){
help=help*10+s[i]-'0';
i++;
}
temp=temp*help;
num.push(temp);
}
else if(s[i]==47){// /
temp=num.top();
num.pop();
j=1;
while(s[i+j]==32) j++;
i=i+j;//开始下一个数
help=0;
while(s[i]>='0'&&s[i]<='9'){
help=help*10+s[i]-'0';
i++;
}
temp=temp/help;
num.push(temp);
}
else{
cout<<"wrong!"<<endl;
}
}
while(!symbol.empty()){
symbol1.push(symbol.top());
symbol.pop();
}
while(!num.empty()){
num1.push(num.top());
num.pop();
}
//后加减
while(!symbol1.empty()){
if(symbol1.top()==1){
temp=num1.top();
num1.pop();
temp=temp+num1.top();
num1.pop();
num1.push(temp);
symbol1.pop();
}
else if(symbol1.top()==-1){
temp=num1.top();
num1.pop();
temp=temp-num1.top();
num1.pop();
num1.push(temp);
symbol1.pop();
}
else{
cout<<"wrong!"<<endl;
}
}
temp=num1.top();
return temp;
}
};
2.官方
由于乘除优先于加减计算,因此不妨考虑先进行所有乘除运算,并将这些乘除运算后的整数值放回原表达式的相应位置,则随后整个表达式的值,就等于一系列整数加减后的值。
基于此,我们可以用一个栈,保存这些(进行乘除运算后的)整数的值。对于加减号后的数字,将其直接压入栈中;对于乘除号后的数字,可以直接与栈顶元素计算,并替换栈顶元素为计算后的结果。
具体来说,遍历字符串 ss,并用变量 \textit{preSign}preSign 记录每个数字之前的运算符,对于第一个数字,其之前的运算符视为加号。每次遍历到数字末尾时,根据 \textit{preSign}preSign 来决定计算方式:
加号:将数字压入栈;
减号:将数字的相反数压入栈;
乘除号:计算数字与栈顶元素,并将栈顶元素替换为计算结果。
代码实现中,若读到一个运算符,或者遍历到字符串末尾,即认为是遍历到了数字末尾。处理完该数字后,更新 preSign 为当前遍历的字符。
遍历完字符串 s 后,将栈中元素累加,即为该字符串表达式的值。
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/basic-calculator-ii/solution/ji-ben-ji-suan-qi-ii-by-leetcode-solutio-cm28/
来源:力扣(LeetCode)
优点:将加减统一为加(减去相反数)。减少内存和时间。
class Solution {
public:
int calculate(string s) {
vector<int> stk;
char preSign = '+';
int num = 0;
int n = s.length();
for (int i = 0; i < n; ++i) {
if (isdigit(s[i])) {//专门判断数字的函数
num = num * 10 + int(s[i] - '0');
}
if (!isdigit(s[i]) && s[i] != ' ' || i == n - 1) {//判断符号,多个判断条件一起考虑
//排除space,确定终止条件
switch (preSign) {
case '+':
stk.push_back(num);
break;
case '-':
stk.push_back(-num);
break;
case '*':
stk.back() *= num;
/*temp=stk.top();
stk.pop();
temp=temp*num;
stk.push(temp);
*/
break;
default:
stk.back() /= num;
}
preSign = s[i];
num = 0;
}
}
return accumulate(stk.begin(), stk.end(), 0);//另一个高级函数,计算栈里面的元素之和
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/basic-calculator-ii/solution/ji-ben-ji-suan-qi-ii-by-leetcode-solutio-cm28/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
总结
之前学习数据结构的时候,在栈的应用那一节,详细讲了如何通过栈进行四则运算,记得还有专门的后缀,前缀写法。现在都已经忘得七七八八了,只记得如果遇到右括号,就一直弹栈,直到弹到第一个左括号为止。
但是面对千奇百怪的问题变形,记得理论什么的可能也没多大用了,一些基本的思想,包括实践的经验还有待慢慢积累练习。。。。。。