4-栈
4.1-栈的实际需求:输入表达式并计算
比如我们输入一个表达式,那么计算机是怎么计算的呢?他怎么知道加减乘除的顺序呢?首先我们要明白的一点是,对于计算机而言,我们输入的表达式,其实是一个字符串,那么计算机在识别表达式并计算的过程中,就使用到了栈
4.2-栈的介绍(stack)
- 栈是操作受限的线性表,操作受限的意思是,栈的取出和压入都只能在一端进行,所以造成的结果就是元素的先进后出,而线性表是指栈中元素之间的逻辑关系是一对一的
- 允许插入和删除的一端,称之为栈顶,而固定无法操作的一端,称之为栈底
- 出栈pop
- 入栈push
- 返回栈顶元素peek
4.3-栈的应用场景
- 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中
- 处理递归调用:和子程序的调用类似,只是除了存储下一个指令的地址外,还将参数,区域变量等数据存入堆栈中
- 表达式的转换(中缀表达式转后缀表达式)与求值(实际解决)
- 二叉树的遍历
- 图的深度优先搜索法
4.4-栈的快速入门:数组模拟栈分析和实现
- 实现栈的思路分析
- 使用数组模拟栈
- 定义top变量表示栈顶,初始化为-1
- 入栈操作:当有数据加入到栈的时候,stack[++top] = data;
- 出栈操作:return stack[top–];
package com.hejiale.Stack.v1;
/**
* 定义一个stack,表示栈
*/
public class ArrayStack {
private int maxSize ;//栈的大小
private int[] stack;//数组,用数组模拟栈,数据就放到该数组中
private int top = -1;//指向栈顶的数据,初始化为-1表示没有数据
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
stack = new int[maxSize];
}
//判断栈满
public boolean isFull(){
// top初始值为-1,每加入一个元素,top会先加1,所以当top等于数组的容量的时候,栈满,因为数组下标从0开始,所以
//top == maxSize - 1;
return top == maxSize - 1;
}
//判断栈空
public boolean isEmpty(){
return top == -1;
}
//入栈
public void push(int value){
if (isFull()){
System.out.println("栈满,不能入栈");
return;
}
stack[++top] = value;
}
//出栈
public int pop(){
if (isEmpty()){
throw new RuntimeException("栈空,不能出栈");
}
return stack[top--];
}
//遍历栈,遍历的时候,需要从栈顶开始显示数据
public void list(){
if (isEmpty()){
System.out.println("栈空,没有数据");
}
for (int i = top ; i>=0;i--){
System.out.print(stack[i]+" ");
}
}
}
4.5-栈实现综合计算器:完成表达式的计算
- 表达式只包含±*/运算符
- ArrayStack中添加以下方法
//返回运算符的优先级,优先级是程序员定的,优先级越高,返回的数字越大
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 == '/';
}
/**
* 计算方法
* 注意:
* 次顶元素 操作符 栈顶元素
* @param num1 栈顶元素
* @param num2 次顶元素
* @param oper 操作符
* @return
*/
public int cal(int num1, int num2, int oper) {
int res = 0;//存放计算的结果
switch (oper) {
case '+':
res = num2 + num1;
break;
case '-':
res = num2 - num1;
break;
case '*':
res = num2 * num1;
break;
case '/':
res = num2 / num1;
break;
default:
break;
}
return res;
}
package com.hejiale.Stack.v2;
/**
* 用栈,实现简单的表达式计算,即表达式的运算符只有+ - * /
*/
public class Calculator {
public static void main(String[] args) {
String expressing = "7+2*6-4";
//1. 首先,准备两个栈,一个数栈,一个符号栈
ArrayStack numberStack = new ArrayStack(10);//数栈
ArrayStack operStack = new ArrayStack(10);//符号栈
//2. 定义需要的辅助遍历
int index = 0;//扫描字符串用到的索引
int num1 = 0;//用来保存栈顶元素
int num2 = 0;//用来保存次顶元素
int oper = 0;//用来保存符号栈返回的符号
int res = 0;//用来保存计算结果
char ch = ' ';//将每次扫描得到的char保存到ch中
//3. 开始扫描
while (true) {
//依次得到expression的每个字符
ch = expressing.charAt(index++);
//判断ch是数字还是符号
if (operStack.isOper(ch)) {
/*
如果ch是符号,则先判断当前符号栈是否为空
如果为空,直接入栈
如果不为空,则依次弹出符号栈中,优先级大于等于当前运算符的运算符,并计算,然后将计算结果压入数栈中
*/
if (!operStack.isEmpty()) {//如果不为空
while(true){
//得到栈顶元素,注意不是出栈pop,仅仅是得到栈顶元素peak
/*
这里出错:出错原因是没有分析operStack为空的情况,因为我认为while循环上一句的if语句
已经判断不为空才进入while循环,所以可以不分析为空的情况,这是不对的,因为如果我们栈中的操作符
都大于当前操作符,那么我们依次弹出符号栈中的全部符号,那么这个时候,如果不进行判空操作的话
我们的operStack.peek()操作就会报越界异常
所以,以后的开发中,一定要考虑全面,时刻注意是否会出现为null的情况,尤其是在if语句中
包含while语句的时候
*/
if(operStack.isEmpty()){
operStack.push(ch);
break;
}else if(operStack.priority(operStack.peek())>=operStack.priority(ch)){
oper = operStack.pop();
num1 = numberStack.pop();//栈顶元素
num2 = numberStack.pop();//次顶元素
res = operStack.cal(num1, num2, oper);//计算并得到结果
numberStack.push(res);//将结果入数栈
}else {
operStack.push(ch);
break;
}
/*if(operStack.priority(operStack.peek())>=operStack.priority(ch)){
oper = operStack.pop();
num1 = numberStack.pop();//栈顶元素
num2 = numberStack.pop();//次顶元素
res = operStack.cal(num1, num2, oper);//计算并得到结果
numberStack.push(res);//将结果入数栈
}else {
operStack.push(ch);
break;
}*/
}
} else {//如果为空
operStack.push(ch);//将扫描到的运算符入栈
}
}else {
numberStack.push(ch - 48);
}
//当表达式扫描完后,结束while循环,但是计算还没有完成,因为有可能符号栈不为空
if(index == expressing.length()){
break;
}
}
while (true){
if (operStack.isEmpty()){
break;
}
oper = operStack.pop();
num1 = numberStack.pop();//栈顶元素
num2 = numberStack.pop();//次顶元素
res = operStack.cal(num1, num2, oper);//计算并得到结果
numberStack.push(res);//将结果入数栈
}
System.out.println(numberStack.pop());
}
}
这里只是实现了一个简单的表达式计算
1. ()等界限符待加入
2. 多位数字的计算待加入,因为当前的程序每次扫描一位字符就进行判断,所以只可以实现个位数的计算