文章目录
224.基本计算器
题目描述
给定一个字符串表达式s
,计算并返回它的值。s
只包含+
,-
,(
,)
,数字,空格。
+
不用用作一元运算(比如+1
和+(2+3)
是无效的);-
可以用作一元运算(比如-1
和-(2+3)
是有效的)。
思路1
自己的思路。双栈,一个操作符栈,一个操作数栈。比较关键的点在于,在什么时候需要执行一次实际的运算?
- 在当前获取到一个数字,并且存在至少一个操作符时,需要运算。
- 在当前字符是
)
时,说明括号内的已经计算完毕,需要和前一个操作数进行计算
为了方便的处理前方不存在第一个操作数的情况,我们用一个int
变量实时保存当前运算的结果。
class Solution {
public int calculate(String s) {
Deque<Character> opStack = new LinkedList<>();
Deque<Integer> numStack = new LinkedList<>();
int sum = 0; // 保存当前的运算结果
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == ' ') continue;
if (c == '(') {
opStack.push('(');
numStack.push(sum); // 把当前的运算结果暂存到操作数栈
sum = 0; // 清零
} else if (c == ')') {
opStack.pop(); // 把 ( 弹出
int a = numStack.isEmpty() ? 0 : numStack.pop();
char op = opStack.isEmpty() ? '+' : opStack.pop();
sum = calc(a, sum, op);
} else if (c == '+' || c == '-') {
opStack.push(c);
} else if (isDigit(c)) {
int num = 0;
int j = i;
while (j < s.length() && isDigit(s.charAt(j))) {
num = num * 10 + s.charAt(j) - '0';
j++;
}
i = j - 1;
if (opStack.isEmpty() || opStack.peek() == '(') sum = num;
else sum = calc(sum, num, opStack.pop());
}
}
return sum;
}
private int calc(int a, int b, char op) {
if (op == '+') return a + b;
return a - b;
}
private boolean isDigit(char c) {
return c >= '0' && c <= '9';
}
}
这个思路和之前做过的一道题目很类似:394.字符串解码
思路2
由于只存在+
和-
,我们考虑可以将括号进行展开。表达式中,除了第一个数字,其余每一个数字的前方,都一定会跟随一个+
或者-
。我们只需要维护,当前的符号是正还是负即可。
而一对括号()
的出现,可能会改变括号内的运算符,所以,每当出现(
时,我们就将当前的运算符压入栈,括号内部的运算符,都需要和栈顶的运算符做一个操作即可。
class Solution {
public int calculate(String s) {
Deque<Integer> ops = new LinkedList<>();
int res = 0;
int sign = 1; // 当前符号为+
ops.push(1); // 先压入一个+号
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == ' ') continue;
if (c == '+') sign = ops.peek();
else if (c == '-') sign = -ops.peek(); // 和栈顶符号做一下运算
else if (c == '(') ops.push(sign); // 压入当前符号
else if (c == ')') ops.pop(); // 前一个(跟随的符号, 可以弹出栈了
else if (isDigit(c)) {
int num = 0;
int j = i;
while (j < s.length() && isDigit(s.charAt(j))) {
num = num * 10 + s.charAt(j) - '0';
j++;
}
i = j - 1;
res += sign * num; // 括号展开直接依次计算
}
}
return res;
}
private boolean isDigit(char c) {
return c >= '0' && c <= '9';
}
}
227.基本计算器II
题目描述
给定一个字符串表达式s
,进行运算并求值。s
中只包含+
,-
,*
,/
和空格。
思路
双栈即可,运算符栈,只能优先级大的压优先级小的。反之需要将运算符出栈,并执行运算。
class Solution {
public int calculate(String s) {
Deque<Integer> nums = new LinkedList<>();
Deque<Character> ops = new LinkedList<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == ' ') continue;
if (isDigit(c)) {
int j = i;
int num = 0;
while (j < s.length() && isDigit(s.charAt(j))) {
num = num * 10 + s.charAt(j) - '0';
j++;
}
i = j - 1;
nums.push(num);
} else {
while (!ops.isEmpty() && getPriority(c) <= getPriority(ops.peek())) {
int b = nums.pop();
int a = nums.pop();
nums.push(calc(a, b, ops.pop()));
}
ops.push(c);
}
}
while (!ops.isEmpty()) {
int b = nums.pop();
int a = nums.pop();
nums.push(calc(a, b, ops.pop()));
}
return nums.pop();
}
private int calc(int a, int b, char c) {
switch(c) {
case '*' : return a * b;
case '/' : return a / b;
case '+' : return a + b;
default : return a - b;
}
}
private int getPriority(char c) {
if (c == '*' || c == '/') return 2;
return 1;
}
private boolean isDigit(char c) {
return c >= '0' && c <= '9';
}
}
对比可以发现,两种计算器的题目,做法有一些区别。
第一题是用一个变量实时保存结果(对于解394这样的题很有用),而第二题则没有进行实时计算(经典的表达式求值的做法,需要考虑运算符优先级)。
follow up
394.字符串解码
题目描述
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
样例
输入:s = “3[a]2[bc]”
输出:“aaabcbc”
思路
主要是需要用一个变量来保存当前已经拼接得到的字符串,然后每当遇到一个数字k
,都会有k[
。此时需要将先前拼接得到的字符串进行暂存,然后对[]
内的字符串处理完毕后,重复k
次,拼接到先前的字符串上。使用StringBuilder
来避免String
对象的频繁创建。
class Solution {
public String decodeString(String s) {
Deque<Integer> repeats = new LinkedList<>(); // 重复次数
Deque<StringBuilder> strings = new LinkedList<>(); // 暂存的字符串
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (isDigit(c)) {
int num = 0;
int j = i;
while (j < s.length() && isDigit(s.charAt(j))) {
num = num * 10 + s.charAt(j) - '0';
j++;
}
i = j - 1;
strings.push(sb);
repeats.push(num);
sb = new StringBuilder();
} else if (c == '[') continue;
else if(c == ']') {
StringBuilder temp = strings.pop();
int r = repeats.pop();
for (int j = 0; j < r; j++) temp.append(sb);
sb = temp;
} else {
sb.append(c);
}
}
return sb.toString();
}
private boolean isDigit(char c) {
return c >= '0' && c <= '9';
}
}