给定一个字符串形式表达式,不使用内置库,将表达式的值求出。
224题只考虑表达式内含有+ - ( )
,而227题只考虑表达式内有+ - * /
没有考虑括号的问题,这里打算写出一个考虑+ - * / ( )
的解决方案,把两题一起做了,这里的-号是减号,不是负号,也不考虑负数。
Implement a basic calculator to evaluate a simple expression string.
The expression string may contain open ( and closing parentheses ), the plus + or minus sign -, non-negative integers and empty spaces .
Example 1:
Input: "1 + 1"
Output: 2
Example 2:
Input: " 2-1 + 2 "
Output: 3
Example 3:
Input: "(1+(4+5+2)-3)+(6+8)"
Output: 23
Note:
You may assume that the given expression is always valid.
Do not use the eval built-in library function.
Implement a basic calculator to evaluate a simple expression string.
The expression string contains only non-negative integers, +, -, *, / operators and empty spaces . The integer division should truncate toward zero.
Example 1:
Input: "3+2*2"
Output: 7
Example 2:
Input: " 3/2 "
Output: 1
Example 3:
Input: " 3+5 / 2 "
Output: 5
Note:
You may assume that the given expression is always valid.
Do not use the eval built-in library function.
思路:
我的思路是比较常规,或者说比较笨的方法。
首先表达式按操作符在操作数的中间,还是在操作数的后面可以分为中缀表达式和后缀表达式。比如1 + 1
就是一个中缀表达式,而1 1 +
则是一个后缀表达式。中缀表达式便于日常使用,但是使用中缀表达式在计算机内求一个表达式值的时候需要考虑优先级,比如* /
法比 + -
法的优先级要高。而如果使用后缀表达式求表达式的值时,不需要考虑运算符的优先级,可以很方便地计算,详细计算过程在后面
所以这题的思路是先考虑将中缀表达式转换为后缀表达式,然后再后缀表达式的基础上求表达式的值。
中缀表达式转换为后缀表达式。这里采用使用栈的方法来进行。
算法思想如下:
从左向右扫描整个表达式
- 如果遇到数字直接将表达式加入后缀表达式
- 遇到运算符时
- 如果遇到 左括号"(",则将左括号入栈
- 如果遇到右括号")", 则将栈内操作符逐一出栈,并且加入后缀表达式,直到遇到左括号"(",并且将左括号删除(出栈)
- 否则遇到的是
+ - * /
,这时如果栈空直接将运算符入栈,如果栈不空,则取出栈顶操作符op1
和当前扫描到的操作符进行比较op2
,如果优先级op1<op2
则将op2
入栈,否则有op1>=op2
弹出当前栈顶操作符并加入后缀表达式,重复这个过程直到栈空,或者遇到左括号"(",或者栈顶元素优先级比当前扫描到的操作符优先级小op1<op2
,这时再将op2
入栈(如果有op1
入栈必须保证有op1 < op2
)
以上就是中缀表达式转后缀表达式的思路,这部分代码如下
中缀表达式转后缀表达式代码,后面为后缀表达式求值过程
int priority(string s1, string s2)//比较两个操作符的优先级,s1表示栈顶操作符,s2表示新到的操作符
{//如果s2优先级比s1大返回正数,相等返回0,否则返回负数
//if (s1 == "#" )//'#'表示开始,任何运算符都比它的优先级低
// return -1;
if (s2 == "(")//直接入栈
return 1;
if (s1 == "(")//当弹出栈内操作符遇到"("时,应该让新来的操作符入栈
return 1;
else if (s1 == s2)
return 0;
else if (s1 == "+" && s2 == "-" || s1 == "-" && s2 == "+")
return 0;
else if (s1 == "*" && s2 == "/" || s1 == "/" && s2 == "*")
return 0;
else if ( (s1 == "+" || s1 == "-") && (s2 == "*" || s2 =="/") )
return 1;
else //if ( (s1 == "*" || s1 == "/") && (s2 == "+" || s2 =="-") )
return -1;
}
//input = "24+2*6/(3-1)" output = (24+((2*6)/(3-1)))-->24 2 6 * 3 1 - / +
vector<string> Infix_expression_to_suffix_expression(const vector<string>& tokens)
{
vector<string> suffix_expression;
stack<string> stk_2; //操作符栈
for(int i = 0; i<tokens.size(); i++)//遇到操作数,直接入栈
{
if (isdigit(tokens[i][0]) || tokens[i].length()>1)//数字
{
suffix_expression.push_back(tokens[i]);//如果是数字直接加入后缀表达式
}
else//遇到操作符
{
if(tokens[i] == ")")//如果运算符是右括号,将栈顶元素出栈直到遇到"(",并且将左括号删除
{
while(stk_2.top() != "(")
{
suffix_expression.push_back(stk_2.top());
stk_2.pop();
}
stk_2.pop();//删除"("
continue;
}
if (stk_2.empty())//第一个操作符直接入栈
{
stk_2.push(tokens[i]);
continue;
}
string op1 = stk_2.top();//第一个操作符
string op2 = tokens[i];//第二个操作符
int prio = priority(op1, op2);
if(prio > 0)//新来的操作符比栈顶操作符优先级高,直接把新来的操作符入栈
{
stk_2.push(tokens[i]);
}
else
{
while(priority(op1, op2) <= 0)//栈顶操作符优先级高,让栈顶操作符出栈加入后缀表达式直到栈顶操作符优先级比新来的操作符优先级低
{
suffix_expression.push_back(stk_2.top());
stk_2.pop();
if (!stk_2.empty())
op1 = stk_2.top();
else break;//栈内所有操作符都比新来的操作符优先级低,导致栈内所有运算符都出栈了
}
stk_2.push(tokens[i]);
}
}
}
while(!stk_2.empty())//将栈内还剩下的操作符加入后缀表达式
{
suffix_expression.push_back(stk_2.top());
stk_2.pop();
}
return suffix_expression;
}
接下来就是计算后缀表达式的值,这个过程非常简单:
后缀表达式计算思路:后缀表达式计算主要需要使用栈,建立一个栈stk
。从左到右读表达式,如果读到操作数就将它压入栈stk
中,如果读到n元运算符(即需要参数个数为n的运算符)则取出由栈顶向下的n项操作数,按操作符运算,再将运算的结果代替原栈顶的n项,压入栈S中 。如果后缀表达式未读完,则重复上面过程,最后输出栈顶的数值则为结束(详细的过程可以看我的这篇博客LeetCode 150. Evaluate Reverse Polish Notation(后缀表达式))
完整代码:
class Solution {
public:
int calculate(string s) {
while(s.find(" ") != string::npos)//去除空格
{
s.replace(s.find(" "), 1, "");
}
vector<string> tok = tokenize_expression(s);
vector<string> suffix_tok = Infix_expression_to_suffix_expression(tok);
int r = evalRPN(suffix_tok);
return r;
}
int compute(int opd1, int opd2, string op)
{
if(op == "+")
return opd1 + opd2;
else if(op == "-")
return opd1 - opd2;
else if(op == "*")
return opd1 * opd2;
else// if(op == "/")
return opd1 / opd2;
}
int evalRPN(vector<string>& tokens) {
stack<int> stk_1;//操作数栈
for(int i = 0; i<tokens.size(); i++)
{
if ( isdigit(tokens[i][0]) || tokens[i].length()>1 )//遇到了数字直接入栈
{
stk_1.push( stoi(tokens[i]) );//c++11 string to interge
}
else//遇到了操作符
{
string op = tokens[i];//栈顶元素
int opd2 = stk_1.top();stk_1.pop();//注意先出栈的是右操作数
int opd1 = stk_1.top();stk_1.pop();
int result = compute(opd1, opd2, op);
stk_1.push(result);//计算结果入操作数栈
}
}//end for loop
return stk_1.top();
}
//for example input = "24+2*6/(3-1)" from string to vector<string>
vector<string> tokenize_expression(const string& expression)//(64-33)/2-1
{
vector<string> tokens;
int start_index = 0, end_index = 0;
for(int i = 0; i<expression.length(); i++)
{
if(!isdigit(expression[i]))
{
end_index = i;
if(end_index - start_index > 0)//最后一个元素
tokens.push_back(expression.substr(start_index, end_index-start_index));//提取两个运算符夹着的数字
tokens.push_back(expression.substr(end_index, 1));//提取运算符
//start_index 和 end_index 指向下一个位置
start_index = end_index+1;
}
else if(i == expression.length()-1)//最后一个是数字
{
end_index = expression.length();
tokens.push_back(expression.substr(start_index, end_index-start_index));
}
}
return tokens;
}
int priority(string s1, string s2)//比较两个操作符的优先级,s1表示栈顶操作符,s2表示新到的操作符
{//如果s2优先级比s1大返回正数,相等返回0,否则返回负数
//if (s1 == "#" )//'#'表示开始,任何运算符都比它的优先级低
// return -1;
if (s2 == "(")//直接入栈
return 1;
if (s1 == "(")//当弹出栈内操作符遇到"("时,应该让新来的操作符入栈
return 1;
else if (s1 == s2)
return 0;
else if (s1 == "+" && s2 == "-" || s1 == "-" && s2 == "+")
return 0;
else if (s1 == "*" && s2 == "/" || s1 == "/" && s2 == "*")
return 0;
else if ( (s1 == "+" || s1 == "-") && (s2 == "*" || s2 =="/") )
return 1;
else //if ( (s1 == "*" || s1 == "/") && (s2 == "+" || s2 =="-") )
return -1;
}
vector<string> Infix_expression_to_suffix_expression(const vector<string>& tokens)
{
vector<string> suffix_expression;
stack<string> stk_2; //操作符栈
for(int i = 0; i<tokens.size(); i++)//遇到操作数,直接入栈
{
if (isdigit(tokens[i][0]) || tokens[i].length()>1)//数字
{
suffix_expression.push_back(tokens[i]);//如果是数字直接加入后缀表达式
}
else//遇到操作符
{
if(tokens[i] == ")")//如果运算符是右括号,将栈顶元素出栈直到遇到"(",并且将左括号删除
{
while(stk_2.top() != "(")
{
suffix_expression.push_back(stk_2.top());
stk_2.pop();
}
stk_2.pop();//删除"("
continue;
}
if (stk_2.empty())//第一个操作符直接入栈
{
stk_2.push(tokens[i]);
continue;
}
string op1 = stk_2.top();//第一个操作符
string op2 = tokens[i];//第二个操作符
int prio = priority(op1, op2);
if(prio > 0)//新来的操作符比栈顶操作符优先级高,直接把新来的操作符入栈
{
stk_2.push(tokens[i]);
}
else
{
while(priority(op1, op2) <= 0)//栈顶操作符优先级高,让栈顶操作符出栈加入后缀表达式直到栈顶操作符优先级比新来的操作符优先级低
{
suffix_expression.push_back(stk_2.top());
stk_2.pop();
if (!stk_2.empty())
op1 = stk_2.top();
else break;//栈内所有操作符都比新来的操作符优先级低,导致栈内所有运算符都出栈了
}
stk_2.push(tokens[i]);
}
}
}
while(!stk_2.empty())//将栈内还剩下的操作符加入后缀表达式
{
suffix_expression.push_back(stk_2.top());
stk_2.pop();
}
return suffix_expression;
}
};
224题结果
227题结果