一、栈的理解
1. 栈的一个实际需求

如计算一个数学表达式可以使用到栈。
**
2.栈的介绍(文字以及图示)


3. 栈的应用场景

二、数组栈的实现
实现说明


实现代码
源代码:**ArrayStackDemo.java**
说明:这里有判断栈已满、判断栈为空、入栈操作、出栈操作、遍历栈的多个方法。
判断栈已满:return top == maxSize-1;
判断栈为空:return top == -1;
入栈操作:判断栈是否满,top++;arrStack[top]=num;
出栈操作:判断栈是否为空,保存现有数据,top位置下移,进行返回
遍历栈:从上到下,top-0 进行遍历
class ArrayStack{//设置栈的最大容量private int maxSize;//创建数组private int[] arrStack;//栈顶默认为-1private int top = -1;public ArrayStack(int maxSize) {//根据参数来开辟空间this.maxSize = maxSize;arrStack = new int[maxSize];}//判断栈是否已满public boolean isFull() {return top == maxSize-1;}//判断栈是否为空public boolean isEmpty() {return top == -1;}//进行入栈操作public void pushStack(int num) {//判断栈是否已满if(isFull()) {System.out.println("栈已满,无法入栈!");return;}//进行入栈操作arrStack[++top] = num;System.out.println("入栈成功");}//进行出栈操作public int popStack() {if(isEmpty()) {throw new RuntimeException("栈空,无法出栈!");}int value = arrStack[top];top--;return value;}//进行遍历栈public void showStack() {//判断栈是否为空if(isEmpty()) {System.out.println("栈为空无法遍历!");return;}System.out.println("栈从上至下结果:");for(int i=top;i>=0;i--) {System.out.println(arrStack[i]+" ");}}}
测试代码:(已测试,能够实现)
package StackExer;import java.util.Scanner;public class ArrayStackDemo {public static void main(String[] args) {ArrayStack arrayStack = new ArrayStack(3);Scanner scanner = new Scanner(System.in);//用于接收进行的操作String str = new String();boolean flag = true;while(flag) {System.out.println("show :展示栈中数据");System.out.println("push :进行入栈操作");System.out.println("pop :进行出栈操作");System.out.println("exit :退出程序");System.out.println();System.out.println("请输入你想要进行的操作:");str = scanner.next();switch (str) {case "show"://遍历栈中的数据arrayStack.showStack();break;case "push"://进行入栈操作System.out.println("请输入您想要入栈的数字:");int num = scanner.nextInt();arrayStack.pushStack(num);break;case "pop"://进行出栈操作try {int value = arrayStack.popStack();System.out.println("出栈的数字为:" + value);} catch (Exception e) {System.out.println(e.getMessage());}break;case "exit":flag = false;break;}}}}
三、链表栈的实现
三步骤(创建节点+方法说明+测试)
首先创建栈的节点:
//创建栈中的节点class Node{public int value;public Node next;public Node(int value) {this.value = value;}}
创建栈:使用链表实现
说明:包含了**判断栈是否为空、栈是否满、入栈操作、出栈操作、遍历栈、获取栈顶元素。
判断栈是否为空:return top==-1;
判断栈是否已满:return top == maxSize-1;
入栈操作:
① 判断栈是否已满
② 上移top,判断top是否为1,是的话创建栈底的元素bottomNode并赋值topNode;
不是第一次那么根据top来使用辅助节点进行上移节点到栈顶位置,temp.next = new Node(val),topNode = new Node(value);
出栈操作:
① 判断是否为空
② 使用辅助节点temp以及事先存储号topNode的值。
③ 使用辅助节点找到出栈节点的前一个,为栈顶元素赋值topNode = temp;,接着删除temp的下一个节点,temp.next=null,最后返回value
获取栈顶元素:return topNode.value
遍历操作:
根据top创建一维数组,遍历链表同时给数组赋值,最后根据top从后往前遍历数组输出即可。
//创建管理栈的类class ListStack{//栈底节点private Node bottomNode;//栈顶节点private Node topNode;//栈的最大容量private int maxSize;//指向最高的位置private int top = -1;//构造器设置栈中的最大容量public ListStack(int maxSize) {this.maxSize = maxSize;}//判断栈为空public boolean isEmpty() {return top == -1;}//判断栈已满public boolean isFull() {return top == maxSize-1;}//进行入栈操作public void pushList(int value) {//判断栈是否已满if(isFull()) {System.out.printf("栈已满,无法入栈!\n\n");return;}top++;//首先上移topif(top == 0) {//创建栈底元素以及设置栈顶元素bottomNode = new Node(value);topNode = new Node(value);}else {//其他插入情况//使用辅助节点Node temp = bottomNode;for(int i=1;i<=top-1;i++) {temp = temp.next;//将辅助节点上移到插入元素之前}//进行入栈操作temp.next = new Node(value);//栈顶也要进行赋值topNode = new Node(value);}System.out.printf("%d入栈成功!\n\n",value);}//进行出栈操作public int popList() {//判断栈中数据是否为空if(isEmpty()) {throw new RuntimeException("栈中数据为空,无法出栈!");}//使用辅助节点Node temp = bottomNode;//变量保存最后栈中元素int value = topNode.value;//指向栈顶位置-1top--;//使temp到达出栈的前一个顶点for(int i=0;i<=top-1;i++) {temp = temp.next;}//更新栈顶元素topNode = temp;if(top==-1) {//出栈最后一个元素时bottomNode = null;topNode = null;}else {temp.next = null;//清除栈顶元素}return value;}//遍历栈public void showList() {//判断是否为空if(isEmpty()) {System.out.printf("栈中数据为空,无法遍历!\n\n");return;}//顶一个辅助节点Node temp = bottomNode;//创建一个数组int []arr = new int[top+1];for(int i=0;i<=top;i++) {arr[i] = temp.value;temp = temp.next;}//进行 反向打印数组的value值for(int i=top;i>=0;i--) {System.out.printf("a[%d]=%d ",i,arr[i]);}System.out.println();}//查看栈顶元素public int getTopNode() {//判断是否为空if(isEmpty()) {throw new RuntimeException("栈中数据为空,无法查看栈顶!");}return topNode.value;}}
测试代码:(已测试成功)
package StackExer;import java.util.Scanner;public class ListStackDemo {public static void main(String[] args) {ListStack listStack = new ListStack(3);Scanner scanner = new Scanner(System.in);//用于接收进行的操作String str = new String();boolean flag = true;while(flag) {System.out.println("show :展示栈中数据");System.out.println("push :进行入栈操作");System.out.println("pop :进行出栈操作");System.out.println("get :获取栈顶元素");System.out.println("exit :退出程序");System.out.println();System.out.println("请输入你想要进行的操作:");str = scanner.next();switch (str) {case "show"://遍历栈中的数据listStack.showList();break;case "push"://进行入栈操作System.out.println("请输入您想要入栈的数字:");int num = scanner.nextInt();listStack.pushList(num);break;case "pop"://进行出栈操作try {int value = listStack.popList();System.out.println("出栈的数字为:" + value);} catch (Exception e) {System.out.println(e.getMessage());}break;case "exit":flag = false;break;case "get":try {int value = listStack.getTopNode();System.out.println("栈顶元素为:" + value);} catch (Exception e) {System.out.println(e.getMessage());}break;}}}}
**
四、栈实现综合计算器(中缀表达式) 实际应用
思路分析(题意+图解)
题意要求
图解分析
① 实现加减乘除(个位数运算+三部分实现)
源代码:Calculator.java
首先是数组栈的实现,增加获取栈顶元素值方法
class ArrayStack2{//设置栈的最大容量private int maxSize;//创建数组private int[] arrStack;//栈顶默认为-1private int top = -1;public ArrayStack2(int maxSize) {//根据参数来开辟空间this.maxSize = maxSize;arrStack = new int[maxSize];}//判断栈是否已满public boolean isFull() {return top == maxSize-1;}//判断栈是否为空public boolean isEmpty() {return top == -1;}//进行入栈操作public void pushStack(int num) {//判断栈是否已满if(isFull()) {System.out.println("栈已满,无法入栈!");return;}//进行入栈操作arrStack[++top] = num;}//进行出栈操作public int popStack() {if(isEmpty()) {throw new RuntimeException("栈空,无法出栈!");}int value = arrStack[top];top--;return value;}//进行遍历栈public void showStack() {//判断栈是否为空if(isEmpty()) {System.out.println("栈为空无法遍历!");return;}System.out.println("栈从上至下结果:");for(int i=top;i>=0;i--) {System.out.println(arrStack[i]+" ");}}//获取栈顶元素public int getTopValue() {if(isEmpty()) {throw new RuntimeException("栈中为空,无法取出");}return arrStack[top];}}
创建一个工具类为之后调用更加简便
包含三个方法:
① 判断是运算符还是数字,返回true为运算符,false为数字
② 根据传入字符来返回优先等级用1,0表示,1表示* / 0为+-,1优先级更高 0优先级低
③ 传入两个数一个运算符,根据运算符进行运算,返回运算值
class Utils{//判断是运算符还是数字public static boolean isOper(char ch) {return ch=='+' || ch=='*' || ch == '-' || ch=='/';}//给出优先级等级public static int priority(int ch) {if(ch == '*' || ch == '/') {return 1;}else if(ch == '+' || ch=='-') {return 0;}else {throw new RuntimeException("无法检测出优先等级");}}//根据运算符对两个数值进行运算public static int operatorNum(int num1,int num2,int oper) {int result = 0;switch (oper) {case '+':result = num1+num2;break;case '-':result = num1-num2;break;case '*':result = num1*num2;break;case '/':result = num1/num2;break;default:break;}return result;}}
实现计算器
前提准备:
使用两个栈,一个用来存放数字,一个用来存放运算符。
代码实现过程:
整体两个部分。
第一个部分是将对应表达式中数字以及运算符入栈,并且处理掉优先级高的运算符;
第二个部分就是处理剩下来优先级低的运算符,将所有运算符进行处理完,数字栈中最后只剩下一个数字那就是最后的结果。
更多细节部分查看上面的思路分析解决
package StackExer;public class Calculator {public static void main(String[] args) {String str = "3*1+2*6-5";//对str进行入栈操作,并且入栈过程中将对应之前优先级高的进行处理int index = 0;//对应str的下标char ch = ' ';//用于取对应下标的字符//取出的两个数字int num1 = 0;int num2 = 0;int oper = 0;//栈中取出的运算符int result = 0;//算运算之和//创建两个栈,一个数字栈、一个存放运算符栈ArrayStack2 numStack = new ArrayStack2(10);ArrayStack2 charStack = new ArrayStack2(10);while(index<str.length()) {ch = str.charAt(index);//1.判断是数字还是运算符if(Utils.isOper(ch)) {//是运算符时//2.判断字符栈中是否为空if(charStack.isEmpty()) {//直接入栈charStack.pushStack(ch);}else {//字符栈中不为空,进行判断优先级//3.待插入运算符与已插入的运算符进行优先级比较if(Utils.priority(ch)<=Utils.priority(charStack.getTopValue())) {//取出字符栈中的运算符oper = charStack.popStack();//注意num值取出的顺序num2 = numStack.popStack();num1 = numStack.popStack();result = Utils.operatorNum(num1, num2, oper);//将求出结果的值入栈numStack.pushStack(result);//将优先级低的入栈charStack.pushStack(ch);}else {//直接将运算符入栈charStack.pushStack(ch);}}}else {//不是运算符numStack.pushStack(ch-'0');}index++;//向后移一位}//处理剩余优先级相同的while(true) {//当字符栈为空时,说明已经结束求值了if(charStack.isEmpty()) {break;}//取出字符栈中的运算符oper = charStack.popStack();//注意num值取出的顺序num2 = numStack.popStack();num1 = numStack.popStack();result = Utils.operatorNum(num1, num2, oper);numStack.pushStack(result);}result = numStack.popStack();System.out.println("表达式结果为:"+result);}}
② 解决①中只能计算个位数的加减乘除(可以多位数)
源代码:Calculator.java
修改过程
说明:修改位置是判断读取是数字时的操作,可以对数字进行拼接。
package StackExer;public class Calculator {public static void main(String[] args) {String str = "3*1+2*600-500";//对str进行入栈操作,并且入栈过程中将对应之前优先级高的进行处理int index = 0;//对应str的下标char ch = ' ';//用于取对应下标的字符//取出的两个数字int num1 = 0;int num2 = 0;int oper = 0;//栈中取出的运算符int result = 0;//算运算之和String keepNum = "";//创建两个栈,一个数字栈、一个存放运算符栈ArrayStack2 numStack = new ArrayStack2(10);ArrayStack2 charStack = new ArrayStack2(10);while(index<str.length()) {ch = str.charAt(index);//1.判断是数字还是运算符if(Utils.isOper(ch)) {//是运算符时//2.判断字符栈中是否为空if(charStack.isEmpty()) {//直接入栈charStack.pushStack(ch);}else {//字符栈中不为空,进行判断优先级//3.待插入运算符与已插入的运算符进行优先级比较if(Utils.priority(ch)<=Utils.priority(charStack.getTopValue())) {//取出字符栈中的运算符oper = charStack.popStack();//注意num值取出的顺序num2 = numStack.popStack();num1 = numStack.popStack();result = Utils.operatorNum(num1, num2, oper);//将求出结果的值入栈numStack.pushStack(result);//将优先级低的入栈charStack.pushStack(ch);}else {//直接将运算符入栈charStack.pushStack(ch);}}}else {//是数字//需要考虑多位数情况//判断是否为最后一位if(index == str.length()-1) {//直接入栈即可numStack.pushStack(ch-'0');}else {//判断下一个是否是运算符if(Utils.isOper(str.charAt(index+1))) {//是的话直接入栈numStack.pushStack(ch-'0');//直接入栈}else {// //不是进行拼接// keepNum = String.valueOf(ch);// index++;// keepNum += str.charAt(index);// //这里进行入栈// numStack.pushStack(Integer.valueOf(keepNum));//首先接收这个数字keepNum = String.valueOf(ch);//进行循环判断拼接数字(下一个不越界同时下一个是数字的情况执行)while((index+1 <= str.length()-1)&&!Utils.isOper(str.charAt(index+1))) {keepNum += str.charAt(index+1);index++;}//将组成的数字入栈numStack.pushStack(Integer.valueOf(keepNum));}}}keepNum = "";//将keepNum进行清空if(index == str.length()-1) {break;}index++;//向后移一位}//处理剩余优先级相同的while(true) {//当字符栈为空时,说明已经结束求值了if(charStack.isEmpty()) {break;}//取出字符栈中的运算符oper = charStack.popStack();//注意num值取出的顺序num2 = numStack.popStack();num1 = numStack.popStack();result = Utils.operatorNum(num1, num2, oper);numStack.pushStack(result);}result = numStack.popStack();System.out.println("表达式结果为:"+result);}}
运行结果:**计算成功**
五、前缀、中缀、后缀表达式(逆波兰表达式) 概述
① 前缀表达式介绍

- 计算流程

② 中缀表达式介绍

③ 后缀表达式介绍

- 求值过程

六 、逆波兰计算器分析与实现
总体源代码:ReversedPolish.java
测试代码:ConfixTest.java
**
① 分析说明(中缀表达式转后缀表达式)
例子说明:
需要通过两个栈进行处理,但为了方便用于存储后缀表达式的栈使用list集合来进行处理
① s1作为栈用来存储运算符或()符号。
② s2由于存储后缀表达式进行出栈取出之后还要进行反转才能获得,所以我们使用list集合来替代。
需要两个方法:
方法一:将String字符串表达式转化为List集合。
方法二:将中缀表达式(list集合)转化为后缀表达式
方法二详细步骤:
首先遍历list获取String字符串item
① 如果item是数字,那么直接保存到s2(list)中。
② 如果item是(,直接保存到s1(栈)中。
③ 如果item是),那么不断出栈存放到s2(list)中,直到栈中的(前,最后将( 符号出栈。
④ 如果item是运算符的话,那么循环比较栈顶以及item的优先级,条件:栈中有内容以及栈中优先级比item优先级大的话,进行出栈放入s2(list)中,最后item放入到s2(list)中;如果item优先级比栈顶大的话直接将item放入s2(list)中。
⑤ 最后遍历完list之后,将栈中的运算符依次出栈到s2(list)集合中保存
代码实现
package StackExer;import java.util.ArrayList;import java.util.List;import java.util.Stack;//逆波兰表达式计算器public class ReversedPolish {//中缀表达式转为后缀表达式的两个方法//① 中缀表达式(String字符串)转为list形式存储数字以及其他运算符public static List<String> toInfixExpressionList(String str) {//当不符合表达式时返回null值的listif(str == null || str == "" || str.length()==0) {return new ArrayList<>();}//创建一个listList<String> list = new ArrayList<>();int index = 0;char ch = 0;String s = "";do {ch = str.charAt(index);//判断为数字还是其他符号if(ch<48 || ch >57) {//是其他符号list.add(""+ch);index++;}else {s="";//首先进行拼接 有个位数或多位数的情况while(index<str.length() && str.charAt(index)>=48 && str.charAt(index)<=57) {s += str.charAt(index);index++;}list.add(s);}}while(index<str.length());return list;}//② 将中缀表达式(list存储)转为后缀表达式public static List<String> infixToSuffixExpression(List<String> list){//创建一个栈、一个list集合 栈用来存放运算符 集合的话用于存放后缀表达式Stack<String> stack = new Stack<>();List<String> SuffixList = new ArrayList<>();for(String item:list) {//如果是数字直接添加入集合if(item.matches("\\d+")) {SuffixList.add(item);}else if(item.equals("(")) {//如果是(,那么直接入栈stack.push(item);}else if(item.equals(")")) {//将栈中的运算符进行出栈直到(前都添加到list中while(!stack.peek().equals("(")) {SuffixList.add(stack.pop());}//将栈中(出栈stack.pop();}else {//剩下来的是优先级的问题//若是item比栈中的优先级小时出栈并添加到集合中while(stack.size()!=0&&Operation.getValue(stack.peek())>=Operation.getValue(item)) {//若栈中有(则不比较直接跳出whileif(Operation.getValue(stack.peek()) == 0) {break;}//将栈中优先级比item大的出栈到list集合中SuffixList.add(stack.pop());}//将item放入list集合中stack.push(item);}}//最后还要将栈中的剩余的添加到list中while(stack.size()!=0) {SuffixList.add(stack.pop());}return SuffixList;}}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 oper) {int result = 0;switch (oper) {case "+":result = ADD;break;case "-":result = SUB;break;case "*":result = MUL;break;case "/":result = DIV;break;default:break;}return result;}}
② 分析说明(后缀表达式求值)

将中缀表达式转为后缀表达式:
(5+3)6+20 => 53+620+
(4+5)/3(3+2) => 45+3/32+
计算思路:
对后缀表达式从左到右进行扫描,遇到数字进行入栈操作,遇到运算符即出栈两次,后出栈数字 对先出栈数字进行运算再将结果入栈,重复操作指导扫描完整个表达式即可,最终栈中只会存在一个那就是结果。
代码实现
需要两个方法:一个是将已编号的后缀表达式转为list集合方式存储;另一个则是进行计算。
这里只需要一个栈即可只用来存放对应数字,根据运算符进行出栈运算即可。
package StackExer;import java.util.ArrayList;import java.util.List;import java.util.Stack;//逆波兰表达式计算器public class ReversedPolish {public static void main(String[] args) {// 首先写一个逆波兰表达式 (5+3)*12-20 => 76String str = "5 3 + 12 * 20 -";// 将String中的各个数转为List集合List<String> list = parseSuffixExpreesionList(str);System.out.println(list);int result = calcuate(list);System.out.println("最后运算结果为:" + result);}//后缀表达式相关的两个方法//① 将普通String类型转化为List类型public static List<String> parseSuffixExpreesionList(String str) {String[] strings = str.split(" ");//创建一个集合List<String> list = new ArrayList<>();for(String s:strings) {list.add(s);}return list;}//② 根据传入的list中进行计算public static int calcuate(List<String> list) {//创建一个栈Stack<String> stack = new Stack<>();int num1 = 0;int num2 = 0;int result = 0;//进行遍历for(String str:list) {if(str.matches("\\d+")) {//匹配1获取多位数字,直接入栈stack.push(str);}else {//从栈中取出两个数num2 = Integer.valueOf(stack.pop());num1 = Integer.valueOf(stack.pop());//获取到运算符 分别进行加减乘除if(str.equals("+")) {result = num1+num2;}else if(str.equals("-")) {result = num1-num2;}else if(str.equals("*")) {result = num1*num2;}else if(str.equals("/")) {result = num1/num2;}else {throw new RuntimeException("无法获取运算符进行运算!");}//还要将计算结果入栈stack.push(String.valueOf(result));}}//最后存在栈中的则是最后运算符结果return Integer.valueOf(stack.pop());}}
运行结果:
总体测试(中缀->后缀->求解)
package com.mjj.test;import java.util.List;import org.junit.Test;import StackExer.ReversedPolish;public class ConfixTest {@Testpublic void test() {String str = "1+((2+3)*40)-5";List<String> list = ReversedPolish.toInfixExpressionList(str);System.out.println("中缀表达式(list):"+list);//[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]List<String> infixToSuffixExpression = ReversedPolish.infixToSuffixExpression(list);System.out.println("后缀表达式(list):"+infixToSuffixExpression);//[1,2,3,+,4,*,+,5,–]//[1, +, 2, +, 3, *, 4, -, 5]//通过后缀表达式来求得结果int result = ReversedPolish.calcuate(infixToSuffixExpression);System.out.println("Expression = "+result);}}
运行结果:
七、逆波兰计算器完整版(参考)
功能说明

源代码
整理者:长路 时间:2020/8/6-2020/8/11
