题目
描述
请写一个整数计算器,支持加减乘三种运算和括号。
数据范围:1000≤∣s∣≤100,保证计算结果始终在整型范围内
要求:空间复杂度: O(n)O(n),时间复杂度 O(n)O(n)
示例1
输入:"1+2"
返回值:3
示例2
输入:"(2(3-4))5"
返回值:-10
示例3
输入:"3+234-1"
返回值:26
代码
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
* 返回表达式的值
* @param s string字符串 待计算的表达式
* @return int整型
*/
#include <string.h>
#include <math.h>
int priority(char a) {
if (a == '*') {
return 2;
} else if (a == '+' || a == '-') {
return 1;
}
return 0;
}
bool isnum(char a) {
if (a >= 48 && a <= 57)
return true;
return false;
}
int solve(char* s ) {
int exp_len = strlen(s); // 表达式长度
char final[exp_len][4]; // 存放生成的后缀表达式
int final_p = 0; // 后缀表达式的指针
char sign[exp_len / 2]; // 运算符栈
int sign_p = 0;
// step 1:转化成逆波兰表达式
for (int i = 0; i < exp_len;) {
if (isnum(s[i])) {
int tmp[3] = {0};
int j = 0;
while (isnum(s[i])) {
tmp[j] = s[i] - '0';
i ++;
j ++;
}
// printf("%d\n",int(pow(2, 3)));
int res = pow(10, j - 1) * tmp[0] + pow(10, j - 2) * tmp[1] + tmp[2];
sprintf(final[final_p ++], "%d", res);
// 加上下面这句会输出乱码,不知道为什么
// final[final_p ++][j] = '\0';
continue;
}
// case 1
else if (sign_p == 0 && priority(s[i]) > 0) {
sign[sign_p ++] = s[i];
printf("add sign:%c\n", sign[sign_p - 1]);
}
// case 4
else if (s[i] == '(') {
sign[sign_p ++] = s[i];
printf("add sign:%c\n", sign[sign_p - 1]);
}
// case 5
else if (s[i] == ')') {
while (sign[sign_p - 1] != '(') {
final[final_p ++][0] = sign[--sign_p];
final[final_p - 1][1] = '\0';
printf("out sign and add final:%s\n", final[final_p - 1]);
}
final[final_p ++][0] = sign[--sign_p];
final[final_p - 1][1] = '\0';
}
// case 2
else if (priority(s[i]) >= priority(sign[sign_p - 1])) {
sign[sign_p ++] = s[i];
printf("add sign:%c\n", sign[sign_p - 1]);
}
// case 3
else if (priority(s[i]) < priority(sign[sign_p - 1])) {
printf("%c:%d < %c:%d\n", s[i], priority(s[i]), sign[sign_p - 1],
priority(sign[sign_p - 1]));
while (sign_p != 0 && sign[sign_p - 1] != '(') {
final[final_p ++][0] = sign[--sign_p];
final[final_p - 1][1] = '\0';
printf("out aign and add final:%s\n", final[final_p - 1]);
}
sign[sign_p ++] = s[i];
} else {
printf("unexpected\n");
}
i ++;
for (int i_ = 0; i_ < sign_p; i_ ++) {
printf("%c", sign[i_]);
}
printf("\n");
}
while (sign_p != 0) {
final[final_p ++][0] = sign[--sign_p];
final[final_p - 1][1] = '\0';
}
// exception : multi minus
for (int i = final_p - 1; i >= 0; i --) {
if (strcmp(final[i], "-") == 0) {
printf("here\n");
int j = i - 1;
while (j >= 0 && strcmp(final[j], "-") == 0) {
strcpy(final[j], "+");
j --;
}
}
}
for (int i = 0; i < final_p; i ++) {
printf("%s ", final[i]);
}
printf("\n\n");
// step 2:逆波兰表达式求值
int op[exp_len]; // 操作数栈
int op_p = 0;
for (int i = 0; i < final_p; i ++) {
printf("stage2: %s\n", final[i]);
if (strcmp(final[i], "+") == 0) {
printf("+:final_p=%d\n", final_p);
int op1 = op[--op_p];
int op2 = op[--op_p];
int tmp = op1 + op2;
printf("stacksize:%d op1:%d op2:%d result:%d\n", op_p, op1, op2, tmp);
printf("%d\n", tmp);
op[op_p ++] = tmp;
} else if (strcmp(final[i], "-") == 0) {
printf("-\n");
int op1 = op[--op_p];
int op2 = op[--op_p];
int tmp = op2 - op1;
printf("stacksize:%d op1:%d op2:%d result:%d\n", op_p, op1, op2, tmp);
printf("%d\n", tmp);
op[op_p ++] = tmp;
} else if (strcmp(final[i], "*") == 0) {
printf("*\n");
int op1 = op[--op_p];
int op2 = op[--op_p];
int tmp = op1 * op2;
printf("stacksize:%d op1:%d op2:%d result:%d\n", op_p, op1, op2, tmp);
printf("%d\n", tmp);
op[op_p ++] = tmp;
} else if (strcmp(final[i], "(") == 0 || strcmp(final[0], ")") == 0) {
continue;
} else {
printf("push %s\n", final[i]);
op[op_p ++] = atoi(final[i]);
}
}
return op[op_p - 1];
}
思路
先将该中缀表达式转化为严格从左到右运算的后缀表达式(逆波兰表达式),然后按照AB4的思路写下去就好了:
【每日算法】AB5 逆波兰表达式
- 为什么要转化为逆波兰表达式
逻辑提问式类似于算术表达式,对于检索而言,这种表达式并不是最优和最简洁的形式,需要进行必要的转换。——百度百科
- 将中缀表达式转化为后缀表达式的方法
准备:一个运算符栈和一个操作数队列
流程:从左向右遍历表达式,遇到操作直接入队,遇到运算符,分为以下几种情况:
(1)运算符栈为空:直接将运算符压进栈中
(2)栈不为空且栈顶运算符优先级低于当前优先级:直接将运算符压进栈中
(3)栈不为空且栈顶运算符优先级高于当前优先级:将栈中运算符依次出栈并入队,然后将当前运算符压入栈中
(4)当前运算符是左括弧:不考虑优先级,直接入栈
(5)当前运算符是右括弧:出栈并压入队列,直到遇到左括弧(括弧不进队列)
(6)遍历结束,出栈并入队
问题
- 由于括号不进队列,因此最好括号拿出来判断,而不是放在优先级里比较
- 关于幂次方,需要使用pow()函数(该函数返回类型是double),C中^是按位相与
- 乱码问题:比如定义字符数组a[4],赋值a[0] = '5',直接输出该数组会有乱码,需要在末尾加上‘\0’,即a[1] = '\0'
- 第三种情况下,当遇到左括号,应该停止出栈
- 对于连续多个-,需要仅保留最后一个-,前面的都变成+
- 之前卡那么久,是因为多个-修正时,忽略了括号的作用,
比如:2-(3-4)-5,
直接转化:2 3 4 - 5 - -
按照原方法修正:2 3 4 + 5 + -
实际应该是:2 3 4 - 5 + -
还是没有完全理解逆波兰,生成逆波兰表达式的时候保留括号,求值时忽略就好了