前言:
本系列博客主要对常用的数据结构及其算法进行描述与相应的代码分析,本篇博客是本系列的第一篇,如果大家能看到最后,相信大家的代码能力有质的飞跃.
文章结构: 代码应用场景 --> 代码思路 --> 具体做法 --> 代码实现 --> 代码分析 --> 总结
代码应用场景:
当我们想要通过一个表达式从而求出表达式的值时,我们就需要栈这种数据结构帮助我们去实现;例如"10 + 20 + 30 " 我们想要得到这个表达式的值,怎么通过代码实现呢?让我们拭目以待
代码思路:
- 我们需要将表达式里面的符号全部拿出来
- 我们应该创建两个栈,一个栈作为数栈(存放数字),一个作为符号栈(存放算数运算符)
- 判断符号的类型,如果时数字直接压入数栈,如果是符号则要进行判断
- 如果符号栈为空,则直接压入符号栈,如果符号栈不为空,则需要判断优先级,若压入符号的优先级大于符号栈栈顶符号的优先级,则直接压入.若小于等于,则符号栈弹栈一个元素,数栈弹栈两个元素进行运算,并将符号压入符号栈
- 将运算的结果重新压入数栈
- 最后数栈中仅剩下一个数字,这个数字就是我们想要的结果
具体做法+代码实现:
- 创建一个栈(数组实现)
public class ArrayStack {
private int maxSize;
pirvate int top;
private int[] stack;
public void push(int element) {
if(isFull()) {
System.out.println("栈已满无法弹栈");
}
stack[++top] = element;
}
public int pop() {
if(isEmpty()) {
throw new Exception("栈已空无法弹栈");
}
}
public boolean isEmpty() {
return top == -1;
}
public boolean isFull() {
return top == maxSize - 1;
}
}
- 创建一个用于判断是否为符号的方法
-
public boolean isOperator(int ch) { if(ch == '+' || ch == '-' || ch == '*' || ch == '/') return true; return false; }
- 创建一个判断优先级的方法
-
public int priority(int ch) { if(ch == '+' || ch =='-') { return 0; }else if(ch == '*' || ch == '/') { reutn 1; } reutn -1; }
- 创建一个计算的方法
public int cal(int num1,int num2,int ope) {
switch(int ope) {
case '+' -> num1 + num2;
case '-' -> num2 - num1;
case '*' -> num1 * num2;
case '/' -> num2 / num1;
}
}
- 主要实现代码
public static int com(String str) {
var numStack = new Stack(20);
var opeStack = new Stack(10);
var ch = 0;
var res = 0;
var num1 = 0;
var num2 = 0;
var ope = 0;
var strBul = new StringBuilder();
var chars = new char[str.length()];
str.getChars(0,str.length(),chars,0);
for (int index = 0; index < chars.length; index++) {
ch = chars[index];
if(opeStack.isOperator(ch)) {
if(opeStack.isEmpty()){
opeStack.push(ch);
}else {
if(opeStack.priority(ch) > opeStack.priority(opeStack.peek())) {
opeStack.push(ch);
} else {
ope = opeStack.pop();
num1 = numStack.pop();
num2 = numStack.pop();
res = numStack.cal(num1,num2,ope);
numStack.push(res);
opeStack.push(ch);
}
}
}else {
strBul.append(ch - '0');
while(true) {
if((index+1 < chars.length) && (!numStack.isOperator(chars[index+1]))) {
strBul.append(chars[++index]);
}else {
numStack.push(Integer.parseInt(strBul.toString()));
strBul.delete(0,strBul.length());
break;
}
}
}
}
while(true) {
if(opeStack.isEmpty()) {
break;
}
ope = opeStack.pop();
num1 = numStack.pop();
num2 = numStack.pop();
res = numStack.cal(num1,num2,ope);
numStack.push(res);
}
return numStack.peek();
}
代码分析:
- 判断优先级,我们可以通过返回的数字去映射他们优先级的大小(如上代码)
- 再进行运算时,我们是通过传过来哦的符号ch来决定他们进行什么样的运算,再把结果返回(此处用了JDK14新特性,读者可自行更改)
- 主要讲述
- ope 表示 符号栈中弹栈的元素
- num1 表示 数字栈中弹栈的第一个元素
- num2 表示 数字栈中弹栈的第二个元素
- ch 表示 在表达式取出来的符号
- 1.首先先要通过length()方法算出来有字符串长度,并new出对应的字符数组
- 2.通过getChars()方法将字符串的字符放入数组中
- 3.通过循环,将对应符号放入ch中(自动类型转换)
- 4.调用isOperator()方法来判断是否是符号,参数应该传入读取出来的ch
- 5.根据思路,如果是符号栈为空直接压入
- 6.根据思路,不为空时要判断优先级,并计算后压入
- 7.若为数字,则需要压入数字栈,需要注意,我们取出来的时字符,并非数字,所以要通过-'0' 变成数字
- 8,stringBuilder的目的:如果是一位多位数,一个一个读取时不可取的,必须将多位数全都读取出来,此时我们判断其后一位是否为数字(要注意数组下标越界异常),通过调用append方法进行字符串拼接,真正实现了一个多位数
- 9,通过while循环将多位数凑齐,调用Interger.praseInt(String s)方法和toString方法把多位数压入
- 10.最后就是通过循环将里面剩余的元素全部弹栈出来计算(符号栈弹一个,数栈弹两个为一次)直到把符号位弹空即方法结束
- 11.最后数栈的栈顶元素就是我们想要的结果
总结:
该数据结构的实现还是得需要一定的Java基础,否则还是有点难自己实现,但篇博客最大的目的是想让读者培养自己的思考能力,按照博客的顺序去思考,如怎么样去写思路,怎么样通过思路写代码
最后,如果还是有看不懂的地方可以私聊我,我会耐心解答,如果大家可以给我三连,我会火速更新栈的实现原理以及逆波兰计算机的实现