这是一个不能操作括弧的算法,只包含加减乘除。
1、使用栈来实现中缀表达式
请输入一个表达式
计算式:7*2*2-5+1-5+3-3
2、思路分析
(1)通过一个 index 值(索引),来遍历表达式,创建两个栈变量,一个是数栈,存放数据;一个数符号栈,存放符号;
(2)如果我们发现是一个数字, 就直接入数栈;
(3)如果发现扫描到是一个符号, 就分如下情况:
(3.1)如果发现当前的符号栈为 空,就直接入栈;
(3.2)如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符, 就需要从数栈中pop出两个数,再从符号栈中pop出一个符号,进行运算,将得到结果入数栈,然后将当前的操作符入符号栈, 如果当前的操作符的优先级大于栈中的操作符, 就直接入符号栈;
(4)当表达式扫描完毕,就顺序的从 数栈和符号栈中pop出相应的数和符号,并运行;
(5)最后在数栈只有一个数字,就是表达式的结果。
这里一定一定一定一定一定一定一定一定一定一定一定要画图。
3、代码
定义一个ArrayStack2类,表示栈的节点
class ArrayStack2{
private int maxSize; //栈的大小
private int[] stack; //数组模拟栈,数据就放在数组中
private int top = -1; //表示栈顶,初始化为-1
//构造方法
public ArrayStack2(int maxSize){
this.maxSize = maxSize;
//初始化数组
this.stack = new int[maxSize];
}
//0、增加一个方法,可以返回当前栈顶的值,但不是真正的pop出来
public int peek(){
return stack[top];
}
//1、判断栈满
public boolean isFull(){
return top==maxSize-1;
}
//2、栈空
public boolean isEmpty(){
return top == -1;
}
//3、入栈
public void push(int value){
//先判断栈是否满
if(isFull()){
System.out.println("栈满......");
return ;
}
//入栈操作
top++; //指针移动
stack[top] = value; //加入数据
}
//4、出栈
public int pop(){
//先判断是否为空
if(isEmpty()){
//抛出异常来处理
throw new RuntimeException("栈空,没有数据......");
}
//出栈操作
int data = stack[top];
top--;
return data;
}
//5、显示栈的情况【遍历栈】
public void show(){
//判断为空
if(isEmpty()){
System.out.println("栈为空......");
return ;
}
for(int i=top;i>-1;i--){
System.out.printf("栈顶到栈底的数据依次是:%d\n",stack[i]);
}
}
//6、返回运算符的优先级,由程序员自己设定,优先级使用数字表示
//数字越大,则优先级就越高【假定运算式中只有加减乘除】
public int priority(int oper){
if(oper == '*' ||oper == '/'){
return 1;
}else if (oper =='+' ||oper=='-'){
return 0;
}else{
return -1;
}
}
//7、判断是不是一个运算符
public boolean isOper(char val){
return val == '*' || val =='/' || val =='+' || val=='-';
}
//8、计算方法
public int cal(int num1,int num2,int oper){
int res = 0; //用于存放计算的结果
switch(oper){
case '+':
res = num1 + num2;
break;
case '-':
res = num2 - num1; //注意顺序
break;
case '*':
res = num1 * num2;
break;
case '/':
res = num2 / num1; //注意顺序
break;
}
return res; //返回计算方法
}
这里需要注意8、计算方法里面的减法和除法数字的顺序。
测试代码
//完成表达式的运算
String expression = "3000+2*6-4"; //多位数有问题???
//创建两个栈,一个数栈,一个符号栈
ArrayStack2 numStack = new ArrayStack2(10);
ArrayStack2 operStack = new ArrayStack2(10);
//定义相关变量
int index = 0; //用于扫描的
int num1 = 0; //存储弹出的数据
int num2 = 0;
int oper = 0; //用于存储操作符
int res = 0; //结果
char ch = ' '; //将每次扫描得到的char保存到ch中
String keepNum =""; //用于拼接多位数
//开始扫描
while(true){
//依次得到expression中的每一个字符
ch = expression.substring(index, index+1).charAt(0); //每次取一个字符
//判断ch是什么,然后做相应的处理
if(operStack.isOper(ch)){ //如果是运算符
//判断符号栈是否为空
if(!operStack.isEmpty()){
//不为空,处理
//如果当前符号的优先级小于或者等于栈中的操作符,就需要从数栈中pop出两个数
//再从符号栈中pop出一个符号,进行运算,得到结果,入数栈,然后将当前的操作符入符号栈
if(operStack.priority(ch) <= operStack.priority(operStack.peek())){
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(num1, num2, oper); //计算结果
//将运算结果入数栈
numStack.push(res);
//将当前符号入符号栈
operStack.push(ch);
}else{
//当前运算符的优先级大于栈中符号的优先级,直接入符号栈
operStack.push(ch);
}
}else{
//为空,直接入符号栈栈
operStack.push(ch);
}
}else{ //如果是数据,直接入数栈,
//numStack.push(ch - 48); //单位数 ascii码
//1、处理多位数的时候,不能发现是一个数就入立即栈,因为可能是多位数
//2、在处理数时,需要向expression的表达式的index后再看一位,如果是数就进行扫描,如果是符号才入栈
//3、因此我们需要定义一个变量 【字符串】,用于拼接
//处理多位数
keepNum = keepNum + ch;
//判断下一个字符是不是数字,如果是数字,就继续扫描,如果是运算符,则入栈
//注意是看后一位,不是index++
//如果ch已经是expression的最后一位,就直接入栈
if(index == expression.length()-1)
{
numStack.push(Integer.parseInt(keepNum));
}else{
if(operStack.isOper(expression.substring(index+1, index+2).charAt(0))){
//如果后一位是运算符,则入栈 keepNum = "1"或者"123"
numStack.push(Integer.parseInt(keepNum));
//重要【清空keepNum】
keepNum = "";
}
}
}
//让index自加
index++;
if(index >= expression.length()){
break;
}
}
//当表达式扫描完后,就顺序的从数栈和符号栈中pop出相应的数和符号,并运行
while(true){
//如果符号栈为空,则计算到最后的结果,数栈中只有一个数字【结果】
if(operStack.isEmpty()){
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(num1, num2, oper); //计算结果
numStack.push(res);
}
//输出
System.out.printf("表达式 %s = %d",expression,numStack.pop());
}
这里难理解就是对多位数的操作
//1、处理多位数的时候,不能发现是一个数就入立即栈,因为可能是多位数
//2、在处理数时,需要向expression的表达式的index后再看一位,如果是数就进行扫描,如果是符号才入栈
//3、因此我们需要定义一个变量 【字符串】,用于拼接
//处理多位数
keepNum = keepNum + ch;
//判断下一个字符是不是数字,如果是数字,就继续扫描,如果是运算符,则入栈
//注意是看后一位,不是index++
//如果ch已经是expression的最后一位,就直接入栈
if(index == expression.length()-1)
{
numStack.push(Integer.parseInt(keepNum));
}else{
if(operStack.isOper(expression.substring(index+1, index+2).charAt(0))){
//如果后一位是运算符,则入栈 keepNum = "1"或者"123"
numStack.push(Integer.parseInt(keepNum));
//重要【清空keepNum】
keepNum = "";
}
}
}
思路:
进到这个else判断中,第一个字符就是数字,先将第一个数字(如果有第二、三......个数字)拼接到keepNum后面,
再判断这个数字字符是不是字符串的最后位置,如果是最后位置直接入数栈,
如果不是最后的位置,再判断index的下一位是不是操作符,如果是操作符,则将拼接的keepNum转为数字再入数栈;
这里隐性的内容就是,如果index+1的位置是数字,则不会入数栈,继续拼接。依次类推。