概括一句话就是: 遇到)或者优先级比即将读入元素低的符号,都要把栈弹出到不能再次弹出为止
符号和运算符可以分为两个栈
运算符的优先级
优先级 | 运算符 |
1 | 括号() |
2 | 负号- |
3 | 乘方** |
4 | 乘*,除/,求余% |
5 | 加+,减- |
6 | 小于<,小于等于<=,大于>,大于等于>= |
7 | 等于==,不等于!= |
8 | 逻辑与&& |
9 | 逻辑或|| |
大致的规律是,一元运算符 > 二元运算符 > 多元运算符。
利用堆栈解析算术表达式的过程
中缀表达式翻译成后缀表达式的方法如下:
(1)从左向右依次取得数据ch。
(2)如果ch是操作数,直接输出。
(3)如果ch是运算符(含左右括号),则:
a:如果ch = '(',放入堆栈。
b:如果ch = ')',依次输出堆栈中的运算符,直到遇到'('为止。
c:如果ch不是')'或者'(',那么就和堆栈顶点位置的运算符top做优先级比较。
1:如果ch优先级比top高,那么将ch放入堆栈。
2:如果ch优先级低于或者等于top,那么输出top,然后将ch放入堆栈。
(4)如果表达式已经读取完成,而堆栈中还有运算符时,依次由顶端输出。
如果我们有表达式(A-B)*C+D-E/F,要翻译成后缀表达式,并且把后缀表达式存储在一个名叫output的字符串中,可以用下面的步骤。
(1)读取'(',压入堆栈,output为空
(2)读取A,是运算数,直接输出到output字符串,output = A
(3)读取'-',此时栈里面只有一个'(',因此将'-'压入栈,output = A
(4)读取B,是运算数,直接输出到output字符串,output = AB
(5)读取')',这时候依次输出栈里面的运算符'-',然后就是'(',直接弹出,output = AB-
(6)读取'*',是运算符,由于此时栈为空,因此直接压入栈,output = AB-
(7)读取C,是运算数,直接输出到output字符串,output = AB-C
(8)读取'+',是运算符,它的优先级比'*'低,那么弹出'*',压入'+",output = AB-C*
(9)读取D,是运算数,直接输出到output字符串,output = AB-C*D
(10)读取'-',是运算符,和'+'的优先级一样,因此弹出'+',然后压入'-',output = AB-C*D+
(11)读取E,是运算数,直接输出到output字符串,output = AB-C*D+E
(12)读取'/',是运算符,比'-'的优先级高,因此压入栈,output = AB-C*D+E
(13)读取F,是运算数,直接输出到output字符串,output = AB-C*D+EF
(14)原始字符串已经读取完毕,将栈里面剩余的运算符依次弹出,output = AB-C*D+EF/-
5 计算算术表达式
当有了后缀表达式以后,运算表达式的值就非常容易了。可以按照下面的流程来计算。
(1)从左向右扫描表达式,一个取出一个数据data
(2)如果data是操作数,就压入堆栈
(3)如果data是操作符,就从堆栈中弹出此操作符需要用到的数据的个数,进行运算,然后把结果压入堆栈
(4)如果数据处理完毕,堆栈中最后剩余的数据就是最终结果。
比如我们要处理一个后缀表达式1234+*+65/-,那么具体的步骤如下。
(1)首先1,2,3,4都是操作数,将它们都压入堆栈
(2)取得'+',为运算符,弹出数据3,4,得到结果7,然后将7压入堆栈
(3)取得'*',为运算符,弹出数据7,2,得到数据14,然后将14压入堆栈
(4)取得'+',为运算符,弹出数据14,1,得到结果15,然后将15压入堆栈
(5)6,5都是数据,都压入堆栈
(6)取得'/',为运算符,弹出数据6,5,得到结果1.2,然后将1.2压入堆栈
(7)取得'-',为运算符,弹出数据15,1.2,得到数据13.8,这就是最后的运算结果.
某些奇葩情况下会让写递归的:
这个时候比较好的思路是找最右边的优先级最低的运算符,这样整个串就被划分成两部分:
#include <stdio.h>
#include <string>
#include <vector>
#include <algorithm>
#include <map>
#include <set>
#include <iostream>
#include <memory.h>
using namespace std;
inline int calTwo(char ch,int a, int b) {
if (ch == '+')
return a+b;
else if (ch == '-')
return a-b;
else if (ch == '*')
return a*b;
else
return a/b;
}
inline int getOp(char ch) {
if (ch == '+' || ch == '-')
return 0;
else if (ch == '*' || ch == '/')
return 1;
else
return 2;
}
int cal(const string& str, int s, int e){
if (str[s] == '(' && str[e] == ')')
return cal(str, s + 1, e - 1);
if (s == e)
return str[s] - '0';
int leftBracket = 0, minPos = s, minOp = 1;
for (int i = s; i <= e; ++i) {
if (leftBracket == 0 && getOp(str[i]) <= minOp) {
minPos = i;
minOp = getOp(str[i]);
}
else if (str[i] == '(')
++leftBracket;
else if (str[i] == ')')
--leftBracket;
}
int left = cal(str, s, minPos - 1);
int right = cal(str, minPos + 1, e);
int res = calTwo(str[minPos],left,right);
return res;
}
int main()
{
string str = "2+1-6*3/(1+1)";
int res = cal(str, 0, str.length() - 1);
return 0;
};
非递归的简洁写法:
#include <iostream>
#include <string>
#include <stack>
using namespace std;
int getPri(char ch) {
if (ch == '(')
return 0;
if (ch == '+' || ch == '-')
return 1;
else if (ch == '*' || ch == '/')
return 2;
else
return 3;
}
int operate(char ch, int x, int y) {
if (ch == '+')
return x + y;
else if (ch == '-')
return x - y;
else if (ch == '*')
return x * y;
else
return x/y;
}
void parse(stack<char>& op, stack<int>& num) {
int x = num.top();
num.pop();
int y = num.top();
num.pop();
int z = operate(op.top(), x, y);
op.pop();
num.push(z);
}
int cal(const string& str) {
stack<int> num;
stack<char> op;
int len = str.length(), i, j;
for (i = 0; i < len; ++i) {
if (str[i] >= '0' && str[i] <= '9')
num.push(str[i]-'0');
else {
if (str[i] == '(')
op.push(str[i]);
else if (str[i] == ')') {
while (op.top() != '(')
parse(op, num);
op.pop();
}
else if (op.empty() || getPri(str[i]) > getPri(op.top()))
op.push(str[i]);
else {
while (!op.empty() || getPri(str[i]) <= getPri(op.top()))
parse(op,num);
op.push(str[i]);
}
}
}
while (!op.empty())
parse(op,num);
return num.top();
}
int main()
{
//string str = "(1+2)*3-4";
//string str = "1+2*3";
//string str = "(1+2)*3/2";
string str = "2*(1+2*5)";
cout<<cal(str)<<endl;
return 0;
}
Python Version:
class Solution:
def FetchNum(self, str, pos, len):
res = 0
while (pos < len and str[pos] >= '0' and str[pos] < '9'):
res = res*10 + (int(str[pos]))
pos += 1
return res, pos-1
def CalValue(self, a, b, op):
if (op == '+'):
return a+b
elif (op == '-'):
return a-b
elif (op == '*'):
return a*b
elif (op == '/'):
return a/b
def CalTop(self, nums, ops):
b = nums.pop()
a = nums.pop()
op = ops.pop()
c = self.CalValue(a, b, op)
return c
def CalMathVal(self, str):
slen = len(str)
i = 0
priDict = {'+':1, '-':1, '*':2, '/':2, '(':0}
nums, ops = [], []
while (i < slen):
if (str[i] >= '0' and str[i] < '9'):
num, i = self.FetchNum(str, i, slen)
nums.append(num)
elif (str[i] in priDict and ((ops and priDict[str[i]] > priDict[ops[-1]]) or (not ops))):
ops.append(str[i])
elif (str[i] == '('):
ops.append(str[i])
elif (str[i] in priDict and ((ops and priDict[str[i]] <= priDict[ops[-1]]) or (not ops)) ):
c = self.CalTop(nums, ops)
nums.append(c)
ops.append(str[i])
elif (str[i] == ')'):
if(ops):
c = self.CalTop(nums, ops)
if (ops[-1] == '('):
ops.pop()
nums.append(c)
i += 1
while (ops):
c = self.CalTop(nums, ops)
nums.append(c)
return nums[-1]
s = Solution()
y = s.CalMathVal("(2+3)*(1+4)")
print(y)
后记:
上述解法并没有考虑单目运算符的情况,如果是单目运算符,只可能是开头-x或者括号内(-x),多一种FetchNum的情况即可。
最后来一道LeetCode题目:
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.
--------------------------------------------
from typing import List
class Solution:
def calculate(self, s: str) -> int:
def fetch_num(s1, pos):
res = 0
while (pos < len(s1) and s1[pos] >= '0' and s1[pos] <= '9'):
res = res * 10 + ord(s1[pos])-ord('0')
pos += 1
return res,pos
def cal_top(n1, o1):
if (len(n1) >= 2 and len(o1) >= 1):
b = n1.pop()
a = n1.pop()
op = o1.pop()
res = a + b if op == '+' else a - b
n1.append(res)
l, i = len(s), 0
nums, ops = [], []
while (i < l):
print("nums={0} ops={1}".format(nums,ops))
if (s[i] >= '0' and s[i] <= '9'):
num, i = fetch_num(s, i)
nums.append(num)
elif (s[i] == '('):
ops.append('(')
i += 1
elif (s[i] in {'+', '-'}):
if (ops and ops[-1] in {'+', '-'}):
cal_top(nums, ops)
ops.append(s[i])
i += 1
elif (s[i] == ')'):
if (ops and ops[-1] == '('): #bug1
ops.pop()
else:
cal_top(nums, ops)
if (ops and ops[-1] == '('): #bug2
ops.pop()
i += 1
else:
i += 1
cal_top(nums, ops)
return nums[-1]
s = Solution()
#print(s.calculate("(7)-(0)+(4)"))
print(s.calculate("(1+(4+5+2)-3)+(6+8)"))