本文主要是介绍利用栈/递归求解算术表达式 LeetCode 224. Basic Calculator,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
概括一句话就是: 遇到)或者优先级比即将读入元素低的符号,都要把栈弹出到不能再次弹出为止
符号和运算符可以分为两个栈
运算符的优先级
优先级 | 运算符 |
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;elsereturn 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;elsereturn 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;elsereturn 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 = 0while (pos < len and str[pos] >= '0' and str[pos] < '9'):res = res*10 + (int(str[pos]))pos += 1return res, pos-1def CalValue(self, a, b, op):if (op == '+'):return a+belif (op == '-'):return a-belif (op == '*'):return a*belif (op == '/'):return a/bdef CalTop(self, nums, ops):b = nums.pop()a = nums.pop()op = ops.pop()c = self.CalValue(a, b, op)return cdef CalMathVal(self, str):slen = len(str)i = 0priDict = {'+':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 += 1while (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 Listclass Solution:def calculate(self, s: str) -> int:def fetch_num(s1, pos):res = 0while (pos < len(s1) and s1[pos] >= '0' and s1[pos] <= '9'):res = res * 10 + ord(s1[pos])-ord('0')pos += 1return res,posdef 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 - bn1.append(res)l, i = len(s), 0nums, 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 += 1elif (s[i] in {'+', '-'}):if (ops and ops[-1] in {'+', '-'}):cal_top(nums, ops)ops.append(s[i])i += 1elif (s[i] == ')'):if (ops and ops[-1] == '('): #bug1ops.pop()else:cal_top(nums, ops)if (ops and ops[-1] == '('): #bug2ops.pop()i += 1else:i += 1cal_top(nums, ops)return nums[-1]s = Solution()
#print(s.calculate("(7)-(0)+(4)"))
print(s.calculate("(1+(4+5+2)-3)+(6+8)"))
这篇关于利用栈/递归求解算术表达式 LeetCode 224. Basic Calculator的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!