文章目录
一、题目
1. 题目描述
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。
2. 示例
示例1:
输入: “3+2*2”
输出: 7
示例2:
输入: " 3/2 "
输出: 1
示例3:
输入: " 3+5 / 2 "
输出: 5
示例4:
输入:“1-1+1”
输出:1
3. 题目解析
见到计算器相关题目就想到了栈,但是处理这种包含运算符号优先级的题目,要小心。
二、个人解法
1. 解法分析
使用两个栈,一个存放数字,一个存放符号
处理方式:
- 如果遇到的是数字,遍历到不是数字为止,将其拼接存到数字栈。
- 如果遇到的是符号,判断符号栈的栈头是否为
*
或者/
,如果是,则先将这个操作符给处理了(数字栈出俩数,配合这个操作符计算一个结果,并将结果入栈),再将遍历到的符号入符号栈;如果不是,则这个符号直接入栈。 - 入栈结束后,遍历符号栈,数字栈配合其进行出栈运算,数字栈的最后一个元素就是我们要的结果。
2. 代码
public int calculate(String s) {
Stack<Integer> st1 = new Stack<>();
Stack<Character> st2 = new Stack<>();
int i = 0;
int temp = 0;
int result = 0;
while (i < s.length()) {
// 去空格
while (i < s.length() && s.charAt(i) == ' ') {
i++;
}
// 将操作符之前的数字拼接成一个整数
while (i < s.length() && s.charAt(i) != '+' && s.charAt(i) != '-' && s.charAt(i) != '*' && s.charAt(i) != '/' && s.charAt(i) != ' ') {
temp = 10 * temp + (s.charAt(i) - '0');
i++;
}
st1.push(temp);
while (i < s.length() && s.charAt(i) == ' ') {
i++;
}
// 遇到操作符
if (i < s.length() && (s.charAt(i) == '+' || s.charAt(i) == '-' || s.charAt(i) == '*' || s.charAt(i) == '/')) {
// 如果栈顶元素是*
if (!st2.empty() && st2.peek() == '*') {
st2.pop();
int temp1 = st1.pop() * st1.pop();
st1.push(temp1);
// st1.push(temp);
temp = 0;
st2.push(s.charAt(i));
// 如果栈顶元素是/
} else if (!st2.empty() && st2.peek() == '/') {
st2.pop();
int temp1 = st1.pop();
int temp2 = st1.pop();
st1.push(temp2 / temp1);
// st1.push(temp);
temp = 0;
st2.push(s.charAt(i));
}
// 其他情况
else {
// st1.push(temp);
temp = 0;
st2.push(s.charAt(i));
}
}
i++;
}
// 遍历操作符栈
while (!st2.empty()) {
char fu = st2.pop();
// 遇到不同的符号进行不同的操作
if (fu == '+') {
int temp1 = st1.pop();
int temp2 = st1.pop();
// result += temp1 + temp2;
st1.push(temp2 + temp1);
} else if (fu == '-') {
int temp1 = st1.pop();
int temp2 = st1.pop();
// result += temp2 - temp1;
st1.push(temp2 - temp1);
} else if (fu == '*') {
int temp1 = st1.pop();
int temp2 = st1.pop();
// result += temp2 * temp1;
st1.push(temp2 * temp1);
} else if (fu == '/') {
int temp1 = st1.pop();
int temp2 = st1.pop();
// result += temp2 / temp1;
st1.push(temp2 / temp1);
}
}
return st1.pop();
}
3. 失败反思
- 严重超时,大概半小时才写出来,并且还是提交失败的错误代码。
- 使用栈来对表达式进行从后往前计算,只考虑了运算符的优先级,未考虑到“1-1-1”、“1-1+1”这样的减号后面是加号或者减号这种情况。在这种情况中,如果从后往前运算,那应该是“1-(1+1)”和“1-(1-1)”即在代码中应该增加这种特殊情况的判断。
4. 改进
改进点
在最后遍历符号栈的时候,对出栈符号是+
或者-
,增加了判断条件,如果此时栈顶元素是-
,那么这个操作符应该取反。
代码
存入栈的代码未改动,只写出有改动的代码:遍历操作符栈部分。
// 遍历操作符栈
while (!st2.empty()) {
char fu = st2.pop();
// 遇到不同的符号进行不同的操作
if (fu == '+') {
int temp1 = st1.pop();
int temp2 = st1.pop();
// 如果此时栈顶元素是-,此符号取反
if(!st2.empty()&&st2.peek()=='-'){
st1.push(temp2 - temp1);
}
else {
st1.push(temp2 + temp1);
}
// result += temp1 + temp2;
} else if (fu == '-') {
int temp1 = st1.pop();
int temp2 = st1.pop();
// 如果此时栈顶元素是-,此符号取反
if(!st2.empty()&&st2.peek()=='-'){
st1.push(temp2 + temp1);
}
else {
st1.push(temp2 - temp1);
}
// result += temp2 - temp1;
// st1.push(temp2 - temp1);
} else if (fu == '*') {
int temp1 = st1.pop();
int temp2 = st1.pop();
// result += temp2 * temp1;
st1.push(temp2 * temp1);
} else if (fu == '/') {
int temp1 = st1.pop();
int temp2 = st1.pop();
// result += temp2 / temp1;
st1.push(temp2 / temp1);
}
}
5. 算法分析
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)
6. 提交截图
(1)第一次失败截图
未考虑-+
这样的符号排列
(2)第二次失败截图
修改完毕后,未考虑到--
这样的符号排列
(3)第三次成功截图
三、官网高星解法
1. 单栈顺序处理法
(1)解法分析
- 给第一个数补
+
号,把每个数和前面的操作符一起存到栈里(假如第一个数是负数,那存栈的第一个数是+0
,第二个数是真正的第一个数) - 如果遇到
*或/
,出栈一个元素,两个元素做完操作后把结果再入栈 - 如果遇到
+或-
,把符号和数拼接后入栈
举例1:
该例中,最终栈内存放 +1、-12、+3
举例2:
该例中,最终栈内存放 +2、-12(-3*4)、+5
(2)代码
public int calculate(String s) {
int result = 0;
Stack<Integer> st1 = new Stack<>();
// 初始化符号变量为+,即假设第一个数不是负数的话给它一个+号
char fu = '+';
int i = 0;
int temp = 0;
while (i < s.length()) {
// 去除前空格
while (i < s.length() && s.charAt(i) == ' ') {
i++;
}
// 连续的数字位拼接,得到待入栈整数
while (i < s.length() && Character.isDigit(s.charAt(i))) {
temp = temp * 10 + (s.charAt(i) - '0');
i++;
}
// 去除空格
while (i < s.length() && s.charAt(i) == ' ') {
i++;
}
// 根据符号,来分支处理考虑这个待入栈整数如何入栈
switch (fu) {
case '+':
st1.push(temp);
break;
case '-':
st1.push(-temp);
break;
case '*':
int temp1 = st1.pop();
st1.push(temp1 * temp);
break;
case '/':
int temp2 = st1.pop();
st1.push(temp2 / temp);
break;
}
// 入栈结束后再处理空格
while (i < s.length() && s.charAt(i) == ' ') {
i++;
}
// 获取下一个数的符号
if (i < s.length()) {
fu = s.charAt(i);
temp = 0;
i++;
}
}
// 栈内所有元素相加即为最终结果
while (!st1.empty())
result += st1.pop();
return result;
}
(3)算法分析
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)
(4)精简代码
使用while循环是为了方便构思遍历到的每位字符。尝试改用for循环可以省去多次去空格。
public int calculate(String s) {
int result=0;
Stack<Integer> st=new Stack<>();
int temp=0;
char fu='+';
for(int i=0;i<s.length();i++){
if(s.charAt(i)==' '){
continue;
}
else if(Character.isDigit(s.charAt(i))){
temp=temp*10+(s.charAt(i)-'0');
}
else {
switch (fu){
case '+':
st.push(temp);
break;
case '-':
st.push(-temp);
break;
case '*':
st.push(st.pop()*temp);
break;
case '/':
st.push(st.pop()/temp);
break;
}
fu=s.charAt(i);
temp=0;
}
}
// 栈内所有元素相加即为最终结果
while (!st.empty())
result += st.pop();
return result;
}
(5)提交截图
这是未精简版代码
这是精简版代码