场景分析
我们要求在输入一串算术表达式之后,点击计算按钮,可以直接输出该算术表达式的结果;
例如我们输入2 *3 * 3-1 ,要想获得结果,计算机是怎么理解这个算式(因为计算机拿到的就是一串字符串)的?
我们需要对该字符 串进行拆分,然后还需要判断哪些地方先运算,哪些地方后运算;
为了解决类似这样的问题,我们就需要使用到栈;
基本介绍
1.栈(stack)是一个先入后出的有序列表;
2.栈是为了限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊性表,允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom);因此我们可以将栈的结构理解为一口井;
3.根据上面栈的定义可以知道,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除;
结构图
如上图所示,入栈(push)是从下往上添加的;
而出栈(pop),则是从上往下取的;
应用场景
1.子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中;
2.处理递归调用:和子程序的调用类似,只是除了存储下一个指令的地址外,也将参数、区域变量等数据存入堆栈中;
3.表达式的转换[中缀表达式转后缀表达式]与求值(实际解决);
4.二叉树的遍历;
5.图形的深度优先(depth-first)搜索法;
数组模拟栈
思路分析
1.定义一个变量来记录栈顶,初始化为-1;
2.当有数据入栈时,top++,stack[top];
3.当有数据出栈时(从栈顶取出一个数据,并且栈顶位置-1),则拿到栈顶的值,然后top--即可;
代码实现
public class StrineStack {
int stack[];
int top;
int maxSize;
static final int DEFAULT_CAPACITY=10;
public StrineStack(int size) {
this.top=-1;
this.stack=new int[size];
this.maxSize=size;
}
public StrineStack() {
}
/**
*
* @Author:Strine
* 入栈操作
* */
public void push(int element){
if (stack.length==0||stack==null){
stack=new int[DEFAULT_CAPACITY];
}
if (isFull()){
System.out.println("当前栈已存满");
return;
}
top++;
stack[top]=element;
}
/**
*
* @Author:Strine
* 出栈操作
* */
public int pop(){
if (stack.length<=0||stack==null){
throw new RuntimeException("当前栈为空,请添加数据后重试");
}
int ret = stack[top];
top--;
return ret;
}
public boolean isFull(){
return top==maxSize-1;
}
}
效果演示
栈实现综合计算器
使用栈完成计算一个算术表达式的结果;
思路分析
1.使用两个栈,第一个栈用来存放数据,第二个栈用来存放运算符;
2.通过一个index值(索引),来遍历我们的表达式;
3.如果我们发现是一个数字,就直接加入到数栈中;如果发现扫描到的是一个符号,就分如下情况:
3.1 如果当前操作符号栈为空,就直接加入到栈中;
3.2 如果当前操作符号栈不为空,则进行比较,如果当前操作符的优先级小于或者等于符号栈中的操作符,那么这时就需要从数栈中取出两个数,然后再从符号栈中取出一个符号,进行运算,将得到的运算结果再放入数栈,再把当前的操作符放入符号栈;
3,3 如果当前操作符的优先级大于栈中的操作符,则直接加入符号栈;
4.当表达式扫描完毕,就顺序的从数栈和符号栈中取出相应的数和符号,进行最后的运算;
5.最后在数栈中只有一个数字,就是最终表达式的结果;
代码实现
/**
* 返回运算符的优先级(我们这里规定数字越大优先级越高)
* */
//假设符号只有加减乘除
public static int priority(int oper){
if (oper=='*'||oper=='/'){
return 1;
}else if (oper=='+'||oper=='-'){
return 0;
}else {
return -1; //符号存在问题,则直接返回-1
}
}
/**
*
* 判断是不是一个运算符
* */
public static boolean isOper(char oper){
return oper=='+'||oper=='-'||oper=='*'||oper=='/';
}
/**
*
* 计算方法
* */
public static int getRet(int num1,int num2,int oper){
//用于存放计算的结果
int ret = 0;
switch (oper){
case '+':
ret=num1+num2;
break;
case '-':
ret=num2-num1;
break;
case '*':
ret=num1*num2;
break;
case '/':
ret=num2/num1;
break;
}
return ret;
}
/**
* @Author:Strine
* 运算方法
* */
public static int calculate(String str){
int length = str.length();
int index = 0; // 用于扫描表达式
int preOper=0;
StrineStack numStack = new StrineStack(length); //创建一个存放数字的栈
StrineStack operStack = new StrineStack(length); //创建一个存放操作符的栈
for (int i = 0; i < length; i++) {
char ele = str.charAt(index);
if (isOper(ele)){
//添加到符号栈中的操作
if (operStack.top==-1){
operStack.push(ele);
}else if (operStack.stack != null){
//如果符号栈不为空
//取出一个符号和两个数组进行运算再将结果放入数字栈中
while (operStack.top!=-1){
preOper=operStack.pop();
int oldPri = priority(preOper);
int newPri = priority(ele);
if (newPri>oldPri){
operStack.push(preOper);//如果新运算符的优先级大于前一个运算符,则直接push进去
break;
}else{
//取出元素进行运算
int num2 = numStack.pop();
int num1 = numStack.pop();
int ret = getRet(num1, num2, preOper);
numStack.push(ret);
}
}
operStack.push(ele);
}
}
else {
//添加到数子栈中的操作
numStack.push(ele-'0');
}
index++;
}
//在扫描字符串完成之后进行最后的运算;
int num1 = numStack.pop();
int num2 = numStack.pop();
int oper = operStack.pop();
int result = getRet(num1, num2, oper);
return result;
}
效果展示