▶ 两个四则表达式运算的题目,第 770 题 Basic Calculator IV 带符号计算不会做 Orz,第 772 题 Basic Calculator III 要收费 Orz。
▶ 自己的全能版代码,28 ms,采用表达式中缀转后缀,然后利用栈来计算。支持加减乘除幂模,一模一样的代码刷掉了这两题,就是效率比较低,28 ms
1 class Solution 2 { 3 public: 4 int calculate(string s)//计算器,输入表达式,输出计算结果 5 { 6 string out; 7 mid_to_last(s, out); 8 return calculate_last(out); 9 } 10 private: 11 int power(int a, int b)// 整数幂运算,a^b 12 { 13 int output; 14 for (output = 1; b > 0; a *= a, b >>= 1) 15 { 16 if (b & 1) 17 output *= a; 18 } 19 return output; 20 } 21 int numread(const string & in, int start, string & output)//读取字符串数字,返回包括前导空白的数字长度 22 { 23 int i, blankLen; 24 for (i = start, blankLen = 0, output = ""; in[i] == ' '; i++, blankLen++);// 跳过前导空白 25 if (in[i] == '-') // -3 26 i++; 27 else if (in[i] == '+') // +3 28 i++, blankLen++; 29 if (in[i]<'0' || in[i]>'9') // 不是数字 30 return 0; 31 for (; in[i] >= '0' && in[i] <= '9'; i++); // 寻找有效字符末尾 32 output = in.substr(start + blankLen, i - start - blankLen); 33 return blankLen + output.size(); 34 } 35 int rankSign(char c)//规定运算等级 36 { 37 switch (c) 38 { 39 case '\0': 40 return -1; 41 case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': 42 return 0; 43 case '(':case ')': 44 return 1; 45 case '+':case '-': 46 return 2; 47 case '*':case '/':case '%': 48 return 3; 49 case '^': 50 return 4; 51 default: 52 return -2; 53 } 54 } 55 bool mid_to_last(const string & in, string & out)//表达式中缀转后缀 56 { 57 int pin, len; 58 string tempStr; 59 stack<char> sym; 60 for (out = "", pin = 0; pin < in.size() && in[pin] != '\0'; pin++) 61 { 62 for (; in[pin] == ' '; pin++); // 处理间隔空格 63 for (; in[pin] == '('; sym.push(in[pin++]));// 连续的 '(' 压栈 64 65 assert(len = numread(in, pin, tempStr)); // 读数字到 tmp 中 66 pin += len; //向后跳位 67 out += tempStr + ' '; // 读取的字符串输出到 out 中 68 69 for (; in[pin] == ' '; pin++); // 处理间隔空格 70 for (; in[pin] == ')'; pin++, sym.pop()) // 遇到 ')',把栈中最近一个 '(' 之后的部分吐空,最后把 '(' 也吐掉 71 for (; sym.top() != '('; out += sym.top(), out += ' ', sym.pop()); 72 73 for (; in[pin] == ' '; pin++); // 处理间隔空格 74 if (in[pin] == '\0') //表达式已空,正常结束,把栈吐空,结束 75 { 76 for (; !sym.empty(); out += sym.top(), out += ' ', sym.pop()); 77 return true; 78 } 79 80 if (sym.empty() || rankSign(in[pin]) > rankSign(sym.top())) //空栈或表达式中算符等级高,压栈 81 sym.push(in[pin]); 82 else 83 { 84 for (; !sym.empty() && rankSign(in[pin]) <= rankSign(sym.top());) //吐到栈中算符等级小于表达式中字符为止 85 out += sym.top(), out += ' ', sym.pop(); 86 sym.push(in[pin]); 87 } 88 } 89 return false; // 表达式在错误的地方结束,报错 90 } 91 int calculate_last(const string & in)//输入后缀表达式计算 92 { 93 int pin, len, tempNum; 94 string tempStr; 95 stack<int> number; 96 for (pin = 0; pin<in.size() && in[pin] != '\0';)// 连着字符和空白一起跳过去 97 { 98 // 取数字 99 if(len = numread(in, pin, tempStr)) 100 { 101 number.push(atoi(tempStr.c_str())); 102 pin += len + 1; 103 continue; 104 } 105 // 处理算符 106 tempNum = number.top(), number.pop(); 107 assert(!number.empty()); // 缺少第二个参与运算的数字 108 switch (in[pin]) 109 { 110 case '+': tempNum = number.top() + tempNum; break; 111 case '-': tempNum = number.top() - tempNum; break; 112 case '*': tempNum = number.top() * tempNum; break; 113 case '/': assert(tempNum), tempNum = number.top() / tempNum; break; // 检查分母非零 114 case '%': assert(tempNum), tempNum = number.top() % tempNum; break; // 检查模数非零 115 case '^': assert(tempNum >= 0), tempNum = power(number.top(), tempNum); break; // 检查指数非负 116 default: 117 assert(false); // 混入了其他符号 118 } 119 number.pop(), number.push(tempNum); 120 pin += 2; 121 } 122 assert(!(number.empty() || number.size() > 1));// 栈空或者栈中有不止一个元素 123 return number.top(); 124 } 125 };
▶ 第 224 题,表达式仅含加减法,支持小括号
● 大佬的代码,13 ms,注意到只含 '+ - ( )' 的表达式可以完全去括号,然后从左向右依次计算,用一个变量 sign 记录当前数字在去括号过程中引入的附加符号
1 class Solution 2 { 3 public: 4 int calculate(string s) 5 { 6 stack<int> st; 7 int i, res, sign, num; 8 for (i = res = 0, sign = 1; i < s.size();) 9 { 10 switch (s[i]) 11 { 12 case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': 13 { 14 for (num = 0; i < s.size() && s[i] >= '0' && s[i] <= '9'; num = (10 * num) + s[i] - '0', i++); 15 res += (sign * num); 16 break; 17 } 18 case ' ': 19 for (; i < s.size() && s[i] == ' '; i++); break; 20 case '(': 21 st.push(res), st.push(sign), res = 0, sign = 1, i++; break; 22 case ')': 23 res = res * st.top(), st.pop(), res += st.top(), st.pop(), i++; break; 24 default: 25 sign = 44 - s[i], i++; break; // 44 - s[i] 等价于 (s[i] == '+') ? 1 : -1,因为 '+' == 43,'-' == 45 26 } 27 } 28 return res; 29 } 30 };
▶ 第227 题,表达式含加减乘除,没有小括号
● 大佬的代码,13 ms,使用栈
1 class Solution 2 { 3 public: 4 int calculate(string s) 5 { 6 const int len = s.size(); 7 stack<int> st; 8 int i, num, temp; 9 char sign; 10 for (i = num = 0, sign = '+'; i < len; i++) 11 { 12 if (s[i] >= '0' && s[i] <= '9') 13 num = num * 10 + s[i] - '0'; 14 if ((s[i] < '0' || s[i] > '9') && s[i] != ' ' || i == len - 1) 15 { 16 if (sign == '-') 17 st.push(-num); 18 else if (sign == '+') 19 st.push(num); 20 else if (sign == '*') 21 temp = st.top(), st.pop(), st.push(temp * num); 22 else if (sign == '/') 23 temp = st.top(), st.pop(), st.push(temp / num); 24 sign = s[i]; 25 num = 0; 26 } 27 } 28 for (temp = 0; !st.empty(); temp += st.top(), st.pop()); 29 return temp; 30 } 31 };
● 大佬的代码,12 ms,与上一个方法算法相同,不使用栈
1 class Solution 2 { 3 public: 4 void trim(string & s) 5 { 6 int index = 0; 7 if (!s.empty()) 8 for (; (index = s.find(' ', index)) != string::npos; s.erase(index, 1)); 9 } 10 int calculate(string s) 11 { 12 trim(s); // 去掉所有空格,原 Java 代码:s = s.trim().replaceAll(" +", ""); 13 int length = s.size(); 14 int i, res; 15 long preVal, curVal; 16 char sign; 17 for (i = res = preVal = 0, sign = '+'; i < length;) 18 { 19 for (curVal = 0; i < length && s[i] >= '0' && s[i] <= '9'; i++) 20 curVal = curVal * 10 + (s[i] - '0'); 21 if (sign == '+') 22 res += preVal, preVal = curVal; 23 else if (sign == '-') 24 res += preVal, preVal = -curVal 25 else if (sign == '*') 26 preVal = preVal * curVal; 27 else if (sign == '/') 28 preVal = preVal / curVal; 29 if (i < length) 30 sign = s[i], i++; 31 } 32 res += preVal; 33 return res; 34 } 35 };
● 大佬的方法,18 ms,三步走战略,先提取 token 替换符号,再计算乘除法,最后计算加减法
1 class Solution 2 { 3 public: 4 int calculate(string s) 5 { 6 if (s.size() == 0) 7 return 0; 8 vector<int> exp, exp1; 9 10 bool isLastDig = false; 11 int i, curIdx, val, last, res; 12 for (i = curIdx = val = 0; i < s.length(); i++)// 第一轮 string 转为 vector<int> 类型的 token,包括计算符 13 { 14 if (s[i] == ' ') 15 continue; 16 val = s[i]; 17 if (val >= '0' && val <= '9') 18 { 19 if (isLastDig) 20 exp[curIdx] = exp[curIdx] * 10 + (int)(val - '0'); 21 else 22 { 23 isLastDig = true; 24 exp.push_back(val - '0'); 25 } 26 } 27 else 28 { 29 isLastDig = false; 30 if (s[i] == '-') // +-*/ 分别用 -2,-1,-3,-4表示 31 exp.push_back(-1); 32 else if (s[i] == '+') 33 exp.push_back(-2); 34 else if (s[i] == '*') 35 exp.push_back(-3); 36 else if (s[i] == '/') 37 exp.push_back(-4); 38 curIdx += 2; 39 } 40 } 41 for (i = 0; i < exp.size(); i++)// 第二轮,进行乘除运算 42 { 43 if (exp[i] == -3) 44 { 45 last = exp1.back(); 46 exp1.pop_back(); 47 exp1.push_back(last * exp[i + 1]); 48 i++; 49 } 50 else if (exp[i] == -4) 51 { 52 last = exp1.back(); 53 exp1.pop_back(); 54 exp1.push_back(last / exp[i + 1]); 55 i++; 56 } 57 else 58 exp1.push_back(exp[i]); 59 } 60 if (exp1.size() == 1)// 只有乘除法的表达式,已经算完了 61 return exp1[0]; 62 for (i = 1, res = exp1[0]; i < exp1.size(); i++)// 第三轮,进行加减运算 63 { 64 if (exp1[i] == -1) 65 res -= exp1[i + 1]; 66 else if (exp1[i] == -2) 67 res += exp1[i + 1]; 68 } 69 return res; 70 } 71 };
● 大佬的神奇方法,19 ms,可能是 VS2015 支持的 C++ 版本低,暂不支持编译
1 class Solution 2 { 3 public: 4 int calculate(string s) 5 { 6 istringstream in('+' + s + '+'); 7 long long total = 0, term = 0, n; 8 char op; 9 while (in >> op) 10 { 11 if (op == '+' || op == '-') 12 { 13 total += term; 14 in >> term; 15 term *= 44 - op; 16 } 17 else 18 { 19 in >> n; 20 if (op == '*') 21 term *= n; 22 else 23 term /= n; 24 } 25 } 26 return total; 27 } 28 };