算法第四版- 1.3 背包、队列和栈
Dijkstra的双栈算术表达式求值
1.课本代码
先贴课本上的测试代码,java转C++
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
double Evaluate(char* a)
{
stack<char> ops;// 存字符
stack<double>vals; //存数字
int i = 0;
double ans = 0;
double v, v1;
char op;
while (a[i] != '\0')
{
switch (a[i])
{
case '(':
break;
case '+':
case '-':
case '*':
case '/':
ops.push(a[i]);
break;
case ')':
op = ops.top();
ops.pop();
v = vals.top();
vals.pop();
v1 = vals.top();
vals.pop();
switch (op)
{
case '+':
v = v1 + v;
break;
case '-':
v = v1 - v;
break;
case '*':
v = v1 * v;
break;
case '/':
v = v1 / v;
break;
}
vals.push(v);
break;
default:
vals.push(double(a[i])-48);
//字符与数字之间ASCII码的转换,bug找了好久。
}
i++;
}
ans=vals.top();
vals.pop();
return ans;
}
//如果中间有sqrt,该如何读进去呢。应该是读到s然后再多读三个字母。不再赘余
//如果不是各位数呢,判断下一位是不是数字,如果是,*10再加上
int main()
{
char a[20] = "(1+((2+3)*(4*5)))";
cout<<Evaluate(a);
return 0;
}
书上写的四种情况很清楚:
1.将操作数压入操作数栈
2.将运算符压入运算符栈
3.忽略左括号
4.当遇到右括号时,弹出一个运算符,弹出所需数量的操作数,并将运算符和操作数的运算结果压入操作数栈。
2.lc第150题,逆波兰表达式
当然,也可以用一个栈去做。
首先先来看一下逆波兰表达式。力扣150题
输入:tokens = [“4”,“13”,“5”,"/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> stk;
int n = tokens.size();
for (int i = 0; i < n; i++) {
string& token = tokens[i];
if (isNumber(token)) {
stk.push(atoi(token.c_str()));
} else {
int num2 = stk.top();
stk.pop();
int num1 = stk.top();
stk.pop();
switch (token[0]) {
case '+':
stk.push(num1 + num2);
break;
case '-':
stk.push(num1 - num2);
break;
case '*':
stk.push(num1 * num2);
break;
case '/':
stk.push(num1 / num2);
break;
}
}
}
return stk.top();
}
bool isNumber(string& token) {
return !(token == "+" || token == "-" || token == "*" || token == "/");
}
};
这个题用string类型,解决了13这种不是个位数的问题。很好,也是原书java中用的方法。
然后中缀转后缀,再看别的博客吧。
然后是第224题和第227题
3.lc第224题
第224题,只有加减和括号,没有乘除
则,我们可以把所有的括号拆开
比如1-(2+3) = 1-2-3
class Solution {
public:
int calculate(string s) {
stack<int> ops;
ops.push(1);
int sign = 1;
//sign是1表示是+号,sign是-1表示是负号
int ret = 0;
int n = s.length();
int i = 0;
while (i < n) {
if (s[i] == ' ') {
i++;
} else if (s[i] == '+') {
sign = ops.top();
i++;
} else if (s[i] == '-') {
sign = -ops.top();
i++;
} else if (s[i] == '(') {
ops.push(sign);
i++;
} else if (s[i] == ')') {
ops.pop();
i++;
} else {
long num = 0;
while (i < n && s[i] >= '0' && s[i] <= '9') {
num = num * 10 + s[i] - '0';
i++;
}//这里解决了非个位数的问题
ret += sign * num;
}
}
return ret;
}
};
4.lc第227题
只有±*/,没有括号
如果有括号,还是用课本上两个栈的好
摘录:
一般需要符号栈、数据栈,两个。但是,看到网上一个写的不错的算法,只用了一个数据栈。符号栈用一个变量sign代替了,只存储上一个符号,主要思想如下:
1.将减法转化为加法(取相反数)
2.由于乘除法优先级高,直接计算
3.整数不仅一位,会>10
4.表达式中没有括号
class Solution {
public:
int calculate(string s) {
vector<int> stk;
char preSign = '+';
int num = 0;
int n = s.length();
for (int i = 0; i < n; ++i) {
if (isdigit(s[i])) {
num = num * 10 + int(s[i] - '0');
}
if (!isdigit(s[i]) && s[i] != ' ' || i == n - 1) {
switch (preSign) {
case '+':
stk.push_back(num);
break;
case '-':
stk.push_back(-num);
break;
case '*':
stk.back() *= num;
break;
default:
stk.back() /= num;
}
preSign = s[i];
num = 0;
}
}
return accumulate(stk.begin(), stk.end(), 0);
}
};
相当于把所有数都压进去,最后累加。
不过压进去之前,* /要先算好,- 也要搞好。
比如"3 + 2*2",压进去就是3,4