关于表达式的知识
- 前缀表达式:运算符都位于操作符之前。
计算机求值:从右到左扫描,遇到数字,压入堆栈,遇到运算符,弹出栈顶和次栈元素。 - 中缀表达式:就是正常的人类计算,但是计算机并不方便。一般会转换为后缀表达式(逆波兰表达式)。
- 后缀表达式:与前缀表达式相似,只是运算符位于操作数之后。
计算机求值:从左到右扫描,遇到数字,压入堆栈,遇到运算符,弹出栈顶和次栈元素,进行运算,结果放回栈。
栈实现计算器
给出中缀表达式,自动计算结果。(不包括括号、小数计算)
- 思路分析
创建数栈、符号栈。
- 通过index(索引)遍历表达式。
- 如果发现是数字,入数栈
- 如果发现是符号 :
3.1 符号栈为空:直接入栈
3.2 符号栈有操作符:
1. 如果当前操作符优先级小于等于栈中操作符,从数栈pop两个数,再从符号栈pop一个操作符。计算结果入数栈,当前操作符入符号栈。
2. 如果当前操作符优先级大于栈中操作符,就直接入符号栈。 - 当表达式扫描完毕,就顺序的从数栈和符号栈pop出相应地数和符号。
代码实现
import java.util.Stack;
/**
* @Author 不知名网友鑫
* @Date 2022/3/10
**/
public class Calculator {
//判断是否为符号。
public static boolean isOper(char ch) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/';
}
//判断运算符优先级。
public static int priority(char ch) {
//优先级高就用1表示。
if (ch == '*' || ch == '/') {
return 1;
} else if (ch == '+' || ch == '-') {
return 0;
}
return -1;
}
//弹出两个数字,一个符号进行计算。
public static int math(int num1, int num2, int oper) {
switch (oper) {
case '+':
return num1 + num2;
case '-':
return num2 - num1;
case '*':
return num1 * num2;
case '/':
return num2 / num1;
default:
break;
}
return -1;
}
public static void main(String[] args) {
StringBuffer s = new StringBuffer();
String expression = "10/2+1-2";
// 1.定义相关变量。
// 数栈、符号栈、索引、取出符号、取出两个数字···
Stack<Integer> numStack = new Stack<>();
Stack<Character> operStack = new Stack<>();
int index = 0; //辅助指针,用来遍历String字符串
int oper = 0;
int num1 = 0;
int num2 = 0;
char ch; //保存每次得到的符号/数字。
while (true) {
if (index >= expression.length()) {
break;
}
//获取index所指的位置。
//刚开始指向String中的第一个,并拆分为char类型。
ch = expression.substring(index, index + 1).charAt(0);
// 1.如果ch是符号
if (isOper(ch)) {
// 1.1如果符号栈为空或者当前运算符优先级大于栈中运算符优先级
if (operStack.isEmpty() || priority(ch) > priority(operStack.peek())) {
operStack.push(ch);
} else {
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
//把计算结果加入到数栈。
numStack.push(math(num1, num2, oper));
//把符号放入到符号栈。
operStack.push(ch);
}
// 2.如果ch是数。
} else {
//用StringBuffer不会产生多个对象,减少内存的损耗。
s.append(ch);
//如果是最后一位,直接加入即可。
if (index == expression.length() - 1) {
numStack.push(Integer.parseInt(s.toString()));
//如果该位的下一位是字符,就将当前s中保存的数字入栈。
} else if (isOper(expression.substring(index + 1, index + 2).charAt(0))) {
numStack.push(Integer.parseInt(s.toString()));
s.delete(0, s.length());
}
}
index++;
}
while (true) {
if (operStack.isEmpty()) {
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
numStack.push(math(num1, num2, oper));
}
System.out.println(numStack.pop());
}
}
栈实现计算器2
给出中缀表达式,转换为后缀表达式并计算结果。(包括括号计算)
- 思路分析
- 将中缀表达式传入ArrayList。
- 将中缀表达式的ArrayList转换为后缀表达式的ArrayList。
- 计算后缀表达式的ArrayList,输出结果。
代码实现
import java.util.ArrayList;
import java.util.Stack;
public class PolandExpression {
public static void main(String[] args) {
String suffixexpression ="1+((2+3)*4)-5";
//将中缀表达式转换为ArrayList数组。
ArrayList<String> s=ToList(suffixexpression);
//中缀ArrayList数组转后缀ArrayList。
ArrayList<String> s1=TosuffixExpression(s);
//通过后缀表达式计算结果。
System.out.println(calculate(s1));
}
public static int priority(String ch){
switch(ch){
case"+":
return 0;
case"-":
return 0;
case"*":
return 1;
case"/":
return 1;
default:
break;
}
return -1;
}
//1. 将中缀表达式转换为对应的ArrayList。
public static ArrayList<String> ToList(String s){
//创建ArrayList存放中缀表达式。
ArrayList<String> a=new ArrayList<>();
int index=0;
do {
StringBuffer sb = new StringBuffer();
//如果此时是运算符,直接加入到ArrayList
if (s.charAt(index) < 48 || s.charAt(index) > 57) {
a.add(s.charAt(index) + "");
index++;
//如果此时是数字。
} else {
//while循环可以处理多位数的问题
while (index < s.length() && s.charAt(index) >= 48 && s.charAt(index) <= 57) {
sb.append(s.charAt(index));
index++;
}
//添加到ArrayList中。
a.add(sb.toString());
//删除sb的内容
sb.delete(0, sb.length());
}
}while(index<s.length());
return a; //返回转换后的ArrayList
}
//2.将中缀表达式的ArrayList转换为后缀表达式的ArrayList。
public static ArrayList<String> TosuffixExpression(ArrayList<String> ls){
//创建两个栈,s1、s2.
Stack<String> s1=new Stack<>();
//由于
ArrayList<String> s2=new ArrayList<>();
//item表示中缀ArrayList中的每一项。
for(String item :ls){
//2.1 如果是一个数字,直接入s2.
if(item.matches("\\d+")){
s2.add(item);
//2.2 如果是左括号,直接入s1.
} else if(item.equals("(")) {
s1.push(item);
//2.3 如果是一个右括号,就弹出s1压入s2,直到遇到左括号。并清空括号。
} else if(item.equals(")") ){
while(!s1.peek().equals("(")){
s2.add(s1.pop());
}
//弹出左括号。
s1.pop();
//2.4 如果当前运算符优先级小于等于s1中栈顶运算符优先级,
// 那么一直弹出s1加入s2,直到当前运算符优先级不小于s1栈顶运算符优先级或弹空。
}else {
while(s1.size()!=0 &&priority(item)<=priority(s1.peek())){
s2.add(s1.pop());
}
s1.push(item);
}
}
//5. s1中剩余的全部放入s2中。
while(s1.size()!=0){
s2.add(s1.pop());
}
return s2;
}
//将后缀表达式转换到ArrayList中。(暂无用)
public static ArrayList<String> change(String s){
ArrayList<String> arrayList=new ArrayList<>();
String [] split=s.split(" ");
for(String s1:split){
arrayList.add(s1);
}
return arrayList;
}
//3. 计算最终的结果(使用后缀表达式)。
//从左到右扫描,遇到数字,压入堆栈,遇到运算符,弹出栈顶和次栈元素,进行运算,结果放回栈。
public static int calculate(ArrayList<String> ls){
Stack<String> stack = new Stack<>();
for(String s:ls){
//正则表达式,匹配的是数字。
if(s.matches("\\d+")){
//如果是数字,直接入栈即可。
stack.push(s);
}else {
//如果是运算符,取出栈中元素,并且计算。
int num1=Integer.parseInt(stack.pop());
int num2=Integer.parseInt(stack.pop());
int res=0;
if(s.equals("+")){
res=num1+num2;
}else if(s.equals("-")){
res=num2-num1;
}else if(s.equals("*")){
res=num1*num2;
}else if(s.equals("/")){
res=num2/num1;
}else {
throw new RuntimeException("Error");
}
//将res转换为字符串。
stack.push(""+ res);
}
}
//返回最后再栈中的结果就是运算的结果。
return Integer.parseInt(stack.pop());
}
}