计算机如何计算数学表达式
平时写的1+2是中缀表达式,对应二叉树的中序遍历
计算机实际计算过程是:先将我们写的中缀表达式转成后缀表达式(二叉树的后序遍历),然后计算的。
中缀转后缀
下面是中缀转后缀的代码,用到了运算符优先级算法,
算法的基本思想:*/的优先级比±高,当前是运算符时,只要栈中的运算符的优先级>=当前运算符,则出栈
注意:表达式对应的语法树一定要画正确,否则可能导致对优先级算法的理解有误!
import java.util.ArrayDeque;
import java.util.Deque;
class Solution {
/**
* 中缀表达式转换为后缀表达式 "3*4-2*5" -> "34*25*-"
* 后缀表达式也就是逆波兰表达式。
*/
private String infixToPostfix(String s) {
StringBuilder sb = new StringBuilder();
Deque<Character> stack = new ArrayDeque<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case '(':
stack.addLast(c);
break;
case ')':
while (!stack.isEmpty()&&stack.peekLast()!='('){
sb.append(stack.removeLast());
}
stack.removeLast(); //(出栈
break;
case '+':
case '-':
while (!stack.isEmpty()&&stack.peekLast()!='('){
sb.append(stack.removeLast());
}
stack.addLast(c);
break;
case '*':
case '/':
while (!stack.isEmpty()&&(stack.peekLast()=='/'||stack.peekLast()=='*')){
sb.append(stack.removeLast());
}
stack.addLast(c);
break;
default:
sb.append(c);
}
}
while (!stack.isEmpty()){
sb.append(stack.removeLast());
}
return sb.toString();
}
}
后缀表达式求值
添加链接描述
得到后缀表达式之后,就可以进行计算了,还是利用栈。
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack=new Stack<>();
for (String s : tokens) {
if(s.length()>1 || Character.isDigit(s.charAt(0))){ //数字
stack.push( Integer.parseInt(s));
}else {
int num2 = stack.pop();
int num1 = stack.pop();
switch (s) {
case "+":
stack.push(num1 + num2);
break;
case "-":
stack.push(num1 - num2);
break;
case "*":
stack.push(num1 * num2);
break;
case "/":
stack.push(num1 / num2);
break;
default:
}
}
}
return stack.pop();
}
}
c++完整代码
int main() {
string target="1+2*3";
stack<char>s;
string ans;
for(auto iter=target.begin();iter!=target.end();iter++){
char c=*iter;
switch(c){
case '(':
s.push('(');
break;
case ')':
while(!s.empty() && s.top()!='('){
char cc=s.top();s.pop();
ans+=cc;
}
s.pop();
break;
case '+':
case '-':
while(s.empty()==false && s.top()!='('){
char cc=s.top();s.pop();
ans+=cc;
}
s.push(c);
break;
case '*':
case '/':
while (s.empty()==false && (s.top()=='*'||s.top()=='/')){
char cc=s.top();s.pop();
ans+=cc;
}
s.push(c);
break;
default:
ans+=c;
}
}
while (!s.empty()){
char cc=s.top();s.pop();
ans+=cc;
}
stack<int>t;
for(auto iter=ans.begin();iter!=ans.end();iter++){
char c=*iter;
if(isdigit(c)){
t.push(c-'0');
}else{
int x=t.top();t.pop();
int y=t.top();t.pop();
switch(c){
case '+':
t.push(y+x);
break;
case '-':
t.push(y-x);
break;
case '/':
t.push(y/x);
break;
case '*':
t.push(y*x);
break;
}
}
}
cout<<t.top();
}
将中缀表达式转成抽象语法树AST
上面是模拟计算机是如何计算的,实际计算机在计算过程中,是将表达式中的每个字符封装成语法树节点,然后组装成抽象语法树,最后遍历这颗语法树求值,下面简单写下封装的逻辑代码
class Solution {
/**
* 利用后缀表达式构造AST
*/
public Node expTree(String s) {
//中缀转后缀
s=infixToPostfix(s);
//封装
Deque<Node>stack=new ArrayDeque<>();
for (int i = 0; i < s.length(); i++) {
char c=s.charAt(i);
Node node = new Node(c);
switch (c){
case '+':
case '-':
case '*':
case '/':
node.right=stack.removeLast();
node.left=stack.removeLast();
break;
default:
}
stack.addLast(node);
}
return stack.pop();
}
/**
* 中缀表达式转换为后缀表达式 "3*4-2*5" -> "34*25-"
* 后缀表达式也就是逆波兰表达式。
*/
private String infixToPostfix(String s) {
StringBuilder sb = new StringBuilder();
Deque<Character> stack = new ArrayDeque<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case '(':
stack.addLast(c);
break;
case ')':
while (!stack.isEmpty()&&stack.peekLast()!='('){
sb.append(stack.removeLast());
}
stack.removeLast(); //(出栈
break;
case '+':
case '-':
while (!stack.isEmpty()&&stack.peekLast()!='('){
sb.append(stack.removeLast());
}
stack.addLast(c);
break;
case '*':
case '/':
while (!stack.isEmpty()&&stack.peekLast()!='('&&stack.peekLast()!='+'&&stack.peekLast()!='-'){
sb.append(stack.removeLast());
}
stack.addLast(c);
break;
default:
sb.append(c);
}
}
while (!stack.isEmpty()){
sb.append(stack.removeLast());
}
return sb.toString();
}
}