一、先做基础计算器
思路分析
1、通过一index值(索引),来遍历我们的表达式
2、如果发现是数字,加入数字栈
3、如果是操作符,分下面三种情况操作
- 如果当前符号栈是空的就直接入栈
- 如果符号栈有操作符就进行比较,如果当前符号优先级小于或等于栈中的操作符,就从数栈中pop两个符号,符号栈中pop出一个进行运算,运算结果入数栈,然后再将当前操作符进栈
- 如果当前的符号大于栈中操作符,就直接符号栈
4、当表达式扫描完毕,就顺序数字栈和符号栈中pop出相应的数和符号并运算
5、最后数字栈只有一个数字,就是结果
代码实现
public class Calculator {
public static void main(String[] args) {
//表达式
String expression ="7+2*6-4";
ArrayStack2 numStack = new ArrayStack2(10);
ArrayStack2 operStack = new ArrayStack2(10);
int index = 0;
int n1=0;
int n2=0;
int oper=0;
int res=0;
char ch = ' ';//将每次扫描到的char保存到ch
//开始扫描表达式
while (true){
//依次得到每个字符
ch = expression.substring(index,index+1).charAt(0);
if (operStack.isOper(ch)){ //如果是运算符
if (!operStack.isEmpty()){
if (operStack.priority(ch)<=operStack.priority(operStack.peek())){
n1 = numStack.pop();
n2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(n1,n2,oper);
//把运算的结果入数栈
numStack.push(res);
//把操作符入符号栈
operStack.push(ch);
}else{
//如果当前操作符优先级大于栈中操作符,直接入符号栈
operStack.push(ch);
}
}else{
//如果符号栈为空,直接加入栈
operStack.push(ch);
}
}else{
//如果是数,入数栈
numStack.push(ch-48);
}
index++;
if (index >= expression.length()){
break;
}
}
while(true){
//如果符号栈为空
if (operStack.isEmpty()){
break;
}
n1= numStack.pop();
n2= numStack.pop();
oper=operStack.pop();
res=numStack.cal(n1,n2,oper);
numStack.push(res);
}
System.out.println("结果为"+numStack.pop());
}
}
class ArrayStack2{
private int maxSize;//栈大小
private int[] stack;//数组,模拟站
private int top=-1;//表示栈顶
public ArrayStack2(int maxSize) {
this.maxSize = maxSize;
stack = new int[this.maxSize];
}
//返回栈顶的值
public int peek(){
return stack[top];
}
//判断栈满
public boolean isFull(){
return top == maxSize-1;
}
//栈空
public boolean isEmpty(){
return top == -1;
}
//入栈
public void push(int value){
//先判断栈是否满
if (isFull()){
System.out.println("栈满了");
return;
}
top++;
stack[top]=value;
}
public int pop(){
if (isEmpty()){
throw new RuntimeException("栈空");
}
int value = stack[top];
top--;
return value;
}
public void list() {
if (isEmpty()) {
System.out.println("栈空没有数据");
return;
}
for (int i = top; i >= 0; i--) {
System.out.println(stack[i] + "\n");
}
}
//优先级 数字越大优先级越高
public int priority(int oper){
if (oper =='*'|| oper=='-'){
return 1;
}else if(oper =='+'||oper == '-'){
return 0;
}else {
return -1;//目前表达式只有+-*/
}
}
//判断是不是符号
public boolean isOper(char val){
return val == '+'||val=='-'||val=='*'||val=='/';
}
//计算方法
public int cal(int n1,int n2,int oper){
int res=0;
switch (oper){
case '+':
res= n1+n2;
break;
case '-':
res= n2-n1;
break;
case '*':
res= n1*n2;
break;
case '/':
res= n2/n1;
break;
}
return res;
}
}
我们发现只能实现一个数字的计算,如果是多位数就不行。
所有当我们检测到是数字的时候,还需要向表达式再看一位,如果是数就继续扫描,如果符号才入栈。因此我们需要定义一个字符串变量,用来拼接多位数
public class Calculator {
public static void main(String[] args) {
//表达式
String expression ="222-4";
ArrayStack2 numStack = new ArrayStack2(10);
ArrayStack2 operStack = new ArrayStack2(10);
int index = 0;
int n1=0;
int n2=0;
int oper=0;
int res=0;
char ch = ' ';//将每次扫描到的char保存到ch
String keepNum = ""; //用来拼接多位数
//开始扫描表达式
while (true){
//依次得到每个字符
ch = expression.substring(index,index+1).charAt(0);
if (operStack.isOper(ch)){ //如果是运算符
if (!operStack.isEmpty()){
if (operStack.priority(ch)<=operStack.priority(operStack.peek())){
n1 = numStack.pop();
n2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(n1,n2,oper);
//把运算的结果入数栈
numStack.push(res);
//把操作符入符号栈
operStack.push(ch);
}else{
//如果当前操作符优先级大于栈中操作符,直接入符号栈
operStack.push(ch);
}
}else{
//如果符号栈为空,直接加入栈
operStack.push(ch);
}
}else{
//如果是数字
//numStack.push(ch-48);
keepNum+=ch;
if (index == expression.length()-1){
numStack.push(Integer.parseInt(keepNum));
}else {
//判断后面一个字符是不是数字
if (numStack.isOper(expression.substring(index+1,index+2).charAt(0))){
//然后后面一位是运算法则入栈
numStack.push(Integer.parseInt(keepNum));
//keepNum
keepNum = "";
}
}
}
index++;
if (index >= expression.length()){
break;
}
}
while(true){
//如果符号栈为空
if (operStack.isEmpty()){
break;
}
n1= numStack.pop();
n2= numStack.pop();
oper=operStack.pop();
res=numStack.cal(n1,n2,oper);
numStack.push(res);
}
System.out.println("结果为"+numStack.pop());
}
}
二、三种表达式介绍
前缀表达式(波兰表达式)
前缀表达式的运算符位于操作数之前。从右向左扫描,遇到数字将数字压入堆栈,遇到符号时,弹出两个栈顶数,用运算符对他们做相应的计算,并将结果入栈;重复直到最左边,最后为结果。
举例 (3+4)×5-6 对应 -×+3456
- 将6、5、4、3压入堆栈
- 遇到+弹出3和4,得7压入栈
- 遇到×弹出7和5,计算出35再入栈
- 最后35-6得29
中缀表达式
就是最常用的表达式(3+4)×5-6
对人很好理解,但是对计算机却不好操作(上面的案例就是)
后缀表达式(逆波兰表达式)
后缀表达式与前缀相似,只不过运算符位于操作数之后
从左到右扫描,遇到数字压入堆栈,遇到运算符,弹出栈顶的两个数,用运算符堆他们进行计算,并结过入栈;重复直到最右,最后得出结果
(3+4)×5-6后缀为34+5×6-
- 将3和4入栈
- 遇到+,弹出3和4,得到7,将7入栈
- 将5入栈
- 遇到×,弹出5和7,算出35,将35入栈
- 将6入栈
- 最后得到-,计算35-6 得到29,得到最终结果
三、逆波兰表达式的计算机
我们想建造一个可以计算的把输入的中缀表达式变为后缀表达式,并进行计算的计算机。
后缀表达式计算
public class PolandNotation {
public static void main(String[] args) {
//先定义一个逆波兰表达式
//为了方便 数字和符号用空格隔开
String suffixExpression = "3 4 + 5 * 6 -";
//将表达式放入ArrayList,遍历集合配合栈完成计算
List<String> List = getListString(suffixExpression);
int res = calculate(List);
System.out.println("逆波兰表达式结果为:"+res);
}
//将逆波兰表达式,依次将数字和运算法放入ArrayList中
public static List<String> getListString(String suffixExpression){
//将suffixExpression分割
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<String>();
for (String ele:split){
list.add(ele);
}
return list;
}
//完成对逆波兰表达式的计算
public static int calculate(List<String> ls){
//创建一个栈
Stack<String> stack = new Stack<>();
for (String item:ls){
if (item.matches("\\d+")){ //匹配的是多位数
//入栈
stack.push(item);
}else {
//pop出两个数并运算再入栈
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
if (item.equals("+")){
res = num1 + num2;
} else if (item.equals("-")) {
res = num1 - num2;
} else if (item.equals("*")) {
res = num1 * num2;
}else if (item.equals("/")) {
res = num1 / num2;
}else {
throw new RuntimeException("符号有问题");
}
stack.push(res+"");
}
}
return Integer.parseInt(stack.pop());
}
}
中缀转后缀思路
1、初始化两个栈,运算符栈s1和存储中间结果的栈s2
2、从左到右扫描中缀表达式
3、遇到操作数,压入s2
4、遇到运算符时,比较其与s1栈顶运算符的优先级
- 如果s1为空或栈顶运算符为"(" 则进s1
- 若优先级比栈顶运算符高,也压入s1
- 否则,将s1栈顶符号压入s2,再重新比较下一个
5、遇到括号时
- 如果左括号,直接进s1
- 如果是右括号,则依次弹出s1栈顶运算符进s2,知道遇到右括号为止,丢弃一对括号
6、重复上面直到最右段
7、将s1剩余依次压入s2
8、依次弹出s2,并逆序 即为后缀表达式
代码实现
将中缀表达式转成对应List
/方法 将中缀表达式转成对应List
public static List<String> toInfixExpression(String s){
ArrayList<String> ls = new ArrayList<>();
int i = 0; //这时是一个指针,用于遍历中缀表达式字符串
String str; //多位数的拼接
char c; //每次遍历一个字符 就放入c
do{
//如果c为非数字,我们就加入到ls
if ((c=s.charAt(i)) < 48 || (c=s.charAt(i))>57){
ls.add(""+c);
i++; //i需要后移
}else{
str = ""; //先将str置成""
while (i<s.length() && (c=s.charAt(i))>=48 && (c=s.charAt(i))<=57){
str += c;//拼接
i++;
}
ls.add(str);
}
}while (i<s.length());
return ls;
}
将中缀表达式变为后缀表达式
public static List<String> parseSuffixExpressionList(List<String> ls){
//定义两个栈
Stack<String> s1 = new Stack<>();//s1栈
//s2整个过程不用pop而且最后还要逆序 我们使用list
ArrayList<String> s2 = new ArrayList<>();
//遍历
for (String item: ls){
//如果是一个数 就加入到s2
if (item.matches("\\d+")){ //正则判断数字
s2.add(item);
} else if (item.equals("(")) {
s1.push(item);
}else if (item.equals(")")){
while (!s1.peek().equals("(")){
s2.add(s1.pop());
}
s1.pop(); //消除s1的左括号
}else{
//当item优先级小于等于栈顶栈顶
while (s1.size()!=0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)){
s2.add(s1.pop());
}
s1.push(item);
}
}
while (s1.size()!=0){
s2.add(s1.pop());
}
return s2;
}
我们还需要一个比较运算符优先级的类
class Operation{
private static int ADD = 1;
private static int SUB = 1;
private static int MUL = 2;
private static int DIV = 2;
//返回对应优先级
public static int getValue(String operation){
int result = 0;
switch (operation){
case "+" :
result = ADD;
break;
case "-" :
result = SUB;
break;
case "*" :
result = MUL;
break;
case "/" :
result = DIV;
break;
default:
System.out.println("不存在输入的符号");
break;
}
return result;
}
}
完整逆波兰表达式计算机
public class PolandNotation {
public static void main(String[] args) {
//完成中缀转后缀
String expression = "1+((2+3)*4)-5";
List<String> infixExpressionList = toInfixExpression(expression);
System.out.println(infixExpressionList);
List<String> parseSuffixExpressionList = parseSuffixExpressionList(infixExpressionList);
System.out.println(parseSuffixExpressionList);
//先定义一个逆波兰表达式
//为了方便 数字和符号用空格隔开
// String suffixExpression = "3 4 + 5 * 6 -";
//将表达式放入ArrayList,遍历集合配合栈完成计算
// List<String> List = getListString(suffixExpression);
int res = calculate(parseSuffixExpressionList);
System.out.println("逆波兰表达式结果为:"+res);
}
//将逆波兰表达式,依次将数字和运算法放入ArrayList中
public static List<String> getListString(String suffixExpression){
//将suffixExpression分割
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<String>();
for (String ele:split){
list.add(ele);
}
return list;
}
//方法 将中缀表达式转成对应List
public static List<String> toInfixExpression(String s){
ArrayList<String> ls = new ArrayList<>();
int i = 0; //这时是一个指针,用于遍历中缀表达式字符串
String str; //多位数的拼接
char c; //每次遍历一个字符 就放入c
do{
//如果c为非数字,我们就加入到ls
if ((c=s.charAt(i)) < 48 || (c=s.charAt(i))>57){
ls.add(""+c);
i++; //i需要后移
}else{
str = ""; //先将str置成""
while (i<s.length() && (c=s.charAt(i))>=48 && (c=s.charAt(i))<=57){
str += c;//拼接
i++;
}
ls.add(str);
}
}while (i<s.length());
return ls;
}
//将中缀转后缀
public static List<String> parseSuffixExpressionList(List<String> ls){
//定义两个栈
Stack<String> s1 = new Stack<>();//s1栈
//s2整个过程不用pop而且最后还要逆序 我们使用list
ArrayList<String> s2 = new ArrayList<>();
//遍历
for (String item: ls){
//如果是一个数 就加入到s2
if (item.matches("\\d+")){ //正则判断数字
s2.add(item);
} else if (item.equals("(")) {
s1.push(item);
}else if (item.equals(")")){
while (!s1.peek().equals("(")){
s2.add(s1.pop());
}
s1.pop(); //消除s1的左括号
}else{
//当item优先级小于等于栈顶栈顶
while (s1.size()!=0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)){
s2.add(s1.pop());
}
s1.push(item);
}
}
while (s1.size()!=0){
s2.add(s1.pop());
}
return s2;
}
//完成对逆波兰表达式的计算
public static int calculate(List<String> ls){
//创建一个栈
Stack<String> stack = new Stack<>();
for (String item:ls){
if (item.matches("\\d+")){ //匹配的是多位数
//入栈
stack.push(item);
}else {
//pop出两个数并运算再入栈
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
if (item.equals("+")){
res = num1 + num2;
} else if (item.equals("-")) {
res = num1 - num2;
} else if (item.equals("*")) {
res = num1 * num2;
}else if (item.equals("/")) {
res = num1 / num2;
}else {
throw new RuntimeException("符号有问题");
}
stack.push(res+"");
}
}
return Integer.parseInt(stack.pop());
}
}
//编写个返回优先级的类
class Operation{
private static int ADD = 1;
private static int SUB = 1;
private static int MUL = 2;
private static int DIV = 2;
//返回对应优先级
public static int getValue(String operation){
int result = 0;
switch (operation){
case "+" :
result = ADD;
break;
case "-" :
result = SUB;
break;
case "*" :
result = MUL;
break;
case "/" :
result = DIV;
break;
default:
System.out.println("不存在输入的符号");
break;
}
return result;
}
}