爨爨爨好

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

▶ 两个四则表达式运算的题目,第 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 };

 

posted on 2018-02-02 11:48  爨爨爨好  阅读(349)  评论(0编辑  收藏  举报