题目:
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。
示例:
输入: "3+2*2"
输出: 7
来源:
解题思路:栈
因为只有加减乘除且正整数,判断逻辑相对简单。
定义2个栈,一个存数字,另一个存操作符且只存+-操作符。
把第一个数字入栈,遍历后面的字符串,操作符与数字将配对出现。
- 如果是加减号,数字入栈,操作符入栈
- 如果是乘除号,栈顶数字与新数字计算并修改栈顶数字
最后遍历操作符的栈,按从头到尾的顺序计算加法或减法。
c代码:
使用数组实现栈,注意栈大小,注意int越界。
char *get(char *s, long *n) {
char *p = s;
while (*p == ' ') p++;
*n = 0;
while (*p >= '0' && *p <= '9') {
*n = *n * 10 + *p - '0';
p++;
}
while (*p == ' ') p++;
return p;
}
// +-*/正整数
int calculate(char * s) {
long num[210000];
char opt[210000];
int topn = -1;
int topo = -1;
long n = 0;
char *p = get(s, &n);
num[++topn] = n;
while (*p != 0) {
char ch = *p;
p = get(p+1, &n);
if (ch == '*') {
num[topn] *= n;
} else if (ch == '/') {
num[topn] /= n;
} else {
num[++topn] = n;
opt[++topo] = ch;
}
}
long ret = num[0];
for (int i = 0; i <= topo; i++) {
if (opt[i] == '+') {
ret += num[i+1];
} else {
ret -= num[i+1];
}
}
return ret;
}
以为空间消耗很大,但从结果上看也不大。
优化思路1:
因为没有括号,所以不需大量存储空间。
当操作符数量等于2时,可以合并栈中的数字。因为此时栈中有且只有3个数,且这3个数是加减关系,所以可以且只可以合并第一个和第二个数字。
优化之后代码如下:
char *get(char *s, long *n) {
char *p = s;
while (*p == ' ') p++;
*n = 0;
while (*p >= '0' && *p <= '9') {
*n = *n * 10 + *p - '0';
p++;
}
while (*p == ' ') p++;
return p;
}
// +-*/正整数
int calculate(char * s) {
long num[3];
char opt[2];
int topn = -1;
int topo = -1;
long n = 0;
char *p = get(s, &n);
num[++topn] = n;
while (*p != 0) {
char ch = *p;
p = get(p+1, &n);
if (ch == '*') {
num[topn] *= n;
} else if (ch == '/') {
num[topn] /= n;
} else {
num[++topn] = n;
opt[++topo] = ch;
if (topo == 1) {
// 合并第一个和第二个数字
if (opt[0] == '+') {
num[0] += num[1];
} else {
num[0] -= num[1];
}
num[1] = num[2];
topn = 1;
opt[0] = opt[1];
topo = 0;
}
}
}
long ret = num[0];
for (int i = 0; i <= topo; i++) {
if (opt[i] == '+') {
ret += num[i+1];
} else {
ret -= num[i+1];
}
}
return ret;
}
运行结果:
优化思路2:
上面代码依旧很复杂,为此再次优化。
优化的思路是:
1,当符号是-号时,把数字变为负数,减号就变成了加号,所以第二个栈全部是加号,没必要存在了。
2,由于第一个栈只需3个空间:一个存储之前的结果,一个存储栈顶元素,一个存储新数字,定义三个变量即可。为了去掉栈顶的指针,只需将字符串s变成0+s即可,保证指针始终指向栈顶。
优化后代码如下:
char *get(char *s, long *n) {
char *p = s;
while (*p == ' ') p++;
*n = 0;
while (*p >= '0' && *p <= '9') {
*n = *n * 10 + *p - '0';
p++;
}
while (*p == ' ') p++;
return p;
}
int calculate(char * s) {
long ret = 0, top = 0;
char *p = get(s, &top); // 将第一个数字放入栈顶top
while (*p != 0) {
char ch = *p;
long n = 0;
p = get(p+1, &n);
if (ch == '*') {
top *= n;
} else if (ch == '/') {
top /= n;
} else {
if (ch == '-') n = 0 - n;
ret += top;
top = n;
}
}
return ret + top;
}
文章先后修改多次,至此应该不会再修改了,除非发现其他解题思路。一件产品需要经过多次锤炼才能更加接近完美。