D3-栈[Java数据结构和算法]
1.栈的介绍stack
1.1是一个先进后出(First In Last Out-FILO)的有序列表
1.2 栈是限制线性表中元素的插入和删除只能在线性表的同一段进行的特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶。
另一端为固定的一端,称为栈底。
1.3 根据栈的定义可知,最后放入栈中元素在栈顶,最先放入的在栈底;删除元素正好相反,最后放入的元素最先删除,最先放入的元素最后删除。
1.4 入栈-push,出栈-pop
1.5 栈的应用场景
(1)子程序的调用:在调往子程序前,会先将下个指令的地址存到堆栈中,知道子程序执行完后再将地址取出,以回到原来的程序。
(2)处理递归调用:和子程序调用类似,只是除了存储下一个指令的地址外,也将参数、区域变量等数据存入堆栈中。
(3)表达式的转换[中缀表达式转后缀表达式]和求值
(4)二叉树的遍历
(5)图形的深度优先搜索法
1.6 实现栈的思路分析
(1)使用数组来模拟栈
(2)定义一个top表示栈顶,初始化-1
(3)入栈的操作,当有数据加入到栈时,top++,stack[top]=data;
(4)出栈的才做 int value=stack[top];top--;return value;
1.7 用数组实现栈
package cn.atguigu.stack;import java.util.Scanner;public class ArrayStackDemo {public static void main(String[] args)throws Exception {// TODO Auto-generated method stub//创建对象ArrayStack arrayStack=new ArrayStack(4);String key="";boolean loop=true;//控制是否需要退出菜单Scanner scanner=new Scanner(System.in);while(loop) {System.out.println("show:表示显示栈");System.out.println("exit:退出程序");System.out.println("push:入栈");System.out.println("pop:出栈");key=scanner.next();switch(key) {case "show":arrayStack.list();break;case "push":System.out.println("请输入一个数:");int value=scanner.nextInt();arrayStack.push(value);break;case "pop":System.out.println("出栈的数字为:");System.out.println(arrayStack.pop());break;case "exit":scanner.close();loop=false;break;}}System.out.println("程序退出");}}//定义ArrayStack表示栈 class ArrayStack{private int maxSize;//栈的大小private int[] stack;//数组,数组模拟栈,数据就放在数组中private int top=-1;//top表示栈顶,初始化为-1//构造器public ArrayStack(int maxSize) {this.maxSize = maxSize;stack =new int[maxSize];}//判断栈是否满public boolean isFull() {return top==maxSize-1;}//判断栈空public boolean isEmpty() {return top==-1;}//进栈public void push(int value) {if(isFull()) {System.out.println("栈满,无法输入");return;}top++;stack[top]=value;}//出栈public int pop() {if(isEmpty()) {throw new RuntimeException("栈空,没有数据");}int value=stack[top];top--;return top;}//显示栈的情况,遍历时,需要从栈顶开始显示数据public void list() {if(isEmpty()) {System.out.println("栈空,没有数据~~~");return;}//需要从栈顶开始显示数据for(int i=top;i>=0;i--) {System.out.printf("stack[%d]=%d\n",i,stack[i]);}}}
2. 使用栈完成计算一个表达式的结果(中缀表达式)
2.1 实现思路
(1)需要两个栈,一个是存放数字的栈numStack,一个存放符号的栈operStack
(2)通过一个index值,来遍历表达式
(3)若index是一个数字,直接入数栈numStack,若扫描到是符号,分情况存储:
-若operStack为空,直接入栈
-若operStack不为空,进行比较
--若当前操作符的优先级小于或等于栈中的操作符,就需要从numStack中pop出两个数,再从operStack中pop出一个符号,进行运算,将得到的结果入numStack,当前操作符入operStack。
--若当前的操作符的优先级大于栈中的操作符,直接入operStack栈
(4)扫描完毕后,顺序地从numStack和operStack中pop出相应的数和符号,并运行。
(5)最后在numStack中只有一个数,就是表达式结果
2.2 源代码
package cn.atguigu.stack;public class Calculator {public static void main(String[] args) {// TODO Auto-generated method stubString expression="30+2*6-2";//创建两个栈,一个为数栈,一个为符号栈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保存到chString keepNum="";//开始while循环的扫描experessionwhile(true) {//依次得到每一个字符ch=expression.substring(index,index+1).charAt(0);//判断ch是什么,然后做相应的处理if(operstack.isOperation(ch)) {//如果ch是运算符if(operstack.isEmpty()) {//如果operStack为空,直接存入operstack.push(ch);//直接存入}else{//如果operStack不为空,需要再判断if(operstack.priority(ch)>operstack.priority(operstack.peek())){//若当前优先级别高于栈中的操作符,直接入栈operstack.push(ch);//再将当前操作符入栈}else {//否则,取出两个数字,进行计算num1=numstack.pop();num2=numstack.pop();oper=operstack.pop();res=numstack.cal(num1, num2, oper);numstack.push(res);//将计算结果入栈operstack.push(ch);//将当前运算符入栈 }}}else {//如果不是运算符,直接入numStack栈//分析思路//1.当处理多位数时,不能发现是一个数就立即入栈,因为他可能是多位数//2.在处理数,需要向expression的表达式index后再看以为,如果是数就进行扫描,如果是符号才进行入栈//3.定义字符串变量,用于拼接//处理多位数keepNum+=ch;//若ch已经是expression的最后一位就直接入栈if(index==expression.length()-1) {numstack.push(Integer.parseInt(keepNum)); }else {//判断下一个字符是不是数字,如果是继续扫描if(operstack.isOperation(expression.substring(index+1, index+2).charAt(0))) {//如果后一位是运算符,入栈keepNum="1"或"123" numstack.push(Integer.parseInt(keepNum));//清空keepNum="";} }}//让index加1,并单独按是否扫描到expression最后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()); } } //先创建一个数组 class ArrayStack2{private int maxSize;//栈的大小private int[] stack;//数组,数组模拟栈,数据就放在数组中private int top=-1;//top表示栈顶,初始化为-1//构造器public ArrayStack2(int maxSize) {this.maxSize = maxSize;stack =new int[maxSize];}//该方法可以返回当前栈顶的值,但是不是真正的poppublic int peek() {return stack[top];}//判断栈是否满public boolean isFull() {return top==maxSize-1;}//判断栈空public boolean isEmpty() {return top==-1;}//进栈public void push(int value) {if(isFull()) {System.out.println("栈满,无法输入");return;}top++;stack[top]=value;}//出栈public int pop() {if(isEmpty()) {throw new RuntimeException("栈空,没有数据");}int value=stack[top];top--;return value;}//显示栈的情况,遍历时,需要从栈顶开始显示数据public void list() {if(isEmpty()) {System.out.println("栈空,没有数据~~~");return;}//需要从栈顶开始显示数据for(int i=top;i>=0;i--) {System.out.printf("stack[%d]=%d\n",i,stack[i]);}}//返回运算符的优先级,优先级由程序员来确定,优先级使用数字表示//数字越大,则优先级越高public int priority(int oper) {if(oper=='*'||oper=='/') {return 1;}else if(oper=='+'||oper=='-') {return 0;}else {return -1;//默认只有加减乘除 }}//判断是不是一个运算符public boolean isOperation(char val) {return (val=='*'||val=='/'||val=='+'||val=='-');}//计算方法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;default:break;}return res;} }
3.前缀,中缀,后缀表达式
3.1 前缀表达式(波兰表达式)
(1)前缀表达式的操作运算符位于操作数之前
-(3+4)*5-6的前缀表达式就是- * + 3 4 5 6
(2)从右到左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对他们做相应的计算,并将结果入栈,重复上述过程直到表达式最左端,最后运算得到的值即为表达式结果
-从右到左扫描,将6,5,4,3入栈,遇到+运算符时,弹出3,4,计算3+4,得到7,将7入栈;遇到*,弹出7和5,得到7*5=35,将35入栈;遇到-,弹出35和 6,计算35-6,得到29,29入栈,得到结果
3.2 中缀表达式
(1)中缀表达式就是常见的运算表达式
(2)对于计算机,一般需要将其转换为后缀表达式进行计算
3.3 后缀表达式(逆波兰表达式)
(1)与前缀表达式相似,只是运算符位于操作数后面
- (3+4)*5-6 3 4 +5 *6 -
a+b a b +
a+(b-c) a b c - +
a+(b-c)*d a b c - d * +
a+d*(b-c) a d b c - * +
a=1+3 a 1 3 + =
(2)从左到右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对其做相应的计算,并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式结果
-从左到右扫描,将3,4压入栈中,遇上+,弹出3,4,得3+4=7,将7压入栈中;将5压入栈中,遇上*;将5,7弹出,计算5*7=35;将35,6压入栈中;遇上-,弹出6,和35,计算35-6=29即为结果;
4.逆波兰计算器
4.1 任务
(1)输入一个逆波兰表达式,使用栈(stack)表示,计算其结果
(2)支持小括号和多位数整数
4.2源代码
package cn.atguigu.stack;import java.util.ArrayList; import java.util.List; import java.util.Stack;public class PolandNotation {public static void main(String[] args) {// TODO Auto-generated method stub//先定义逆波兰表达式//(3+4)*5-6--> 3 4 + 5 * 6 -//说明:为了方便,逆波兰表达式中的数字和符号使用空格隔开String suffixExpression="3 4 + 5 * 6 -";//思路//1.先将表达式放入ArrayList中//2.将ArrayList传递给一个方法,遍历ArrayList配合栈完成计算List<String> rpnlist=getListString(suffixExpression);System.out.println(rpnlist);int res=calculate(rpnlist);System.out.println(res);} //将一个逆波兰表达式,依次将数据和运算符放入到 ArrayList找那个public static List<String> getListString(String suffixExpression){//将suffixExpression分割String[] split=suffixExpression.split(" ");List<String> list=new ArrayList<String>();for(String ele:split) {list.add(ele);}return list;}//完成对逆波兰表达式的运算public static int calculate(List<String> ls) {//创建栈,只需要一个栈即可Stack<String> stack=new Stack<String>();//遍历lsfor(String item:ls) {//使用正则表达式取出数if(item.matches("\\d+")) {//匹配的是多位数//入栈 stack.push(item);}else {//pop出两个数,并运算,再入栈int num2=Integer.parseInt(stack.pop());int num1=Integer.parseInt(stack.pop());int res=0;if(item.equals("+")) {res=num1+num2;}else if(item.equals("-")) {res=num1-num2;}else if(item.equals("*")) {res=num1*num2;}else if(item.equals("/")) {res=num1/num2;}else {throw new RuntimeException("运算符有误");}stack.push(res+"");}}//最后留在stack中的数据就是运算结果return Integer.parseInt(stack.pop());} }
4.3 将中缀表达式转换为后缀表达式
(1)实现步骤:
-step1:初始化两个栈:运算符栈s1和存储中间结果的栈s2
-step2:从左到右扫描中缀表达式;
-step3:遇到操作数时,将其压入s2
-step4:遇到运算符时,比较其与s1栈顶运算符的优先级
--若s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
--否则,若优先级比栈顶运算符的高,也将运算符压入s1;
--否则,将s1栈顶的运算符弹出并压入到s2中,再次转到与s1中新的栈顶运算符相比较;
-step5:遇到括号时
--若是左括号,则直接压入栈
--若是有括号,则依次弹出s1栈顶的运算符,并压入s2,直到左括号位置,此时将这一对括号丢弃
-step6:重复步骤2-5,直到表达式最右边
-step7:将s1中剩余的运算符依次弹出并压入s2
-step8:依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
(2)源代码
package cn.atguigu.stack;import java.util.ArrayList; import java.util.List; import java.util.Stack;public class PolandNotation {//完成讲一个中缀表达式转成后缀表达式的功能//说明//1.(3+4)*5-6--> 3 4 + 5 * 6 -//2.因为直接对字符串进行操作,不方便,因此先将字符串准成中缀的表达式对应的List // String expression="(3+4)*5-6";public static void main(String[] args) {// TODO Auto-generated method stub//先定义逆波兰表达式//(3+4)*5-6--> 3 4 + 5 * 6 -//说明:为了方便,逆波兰表达式中的数字和符号使用空格隔开 String suffixExpression="3 4 + 5 * 6 -"; String Expression="1+((2+3)*4)-5";List<String> infixExpressionList=toInfixExpressionList(Expression);System.out.println("中缀表达式List="+infixExpressionList);List<String> parseSuffixExpressionList=parseSuffixExpression(infixExpressionList);System.out.println("后缀表达式List="+parseSuffixExpressionList);int res=calculate(parseSuffixExpressionList); System.out.println("结果为="+res);/* * //思路 //1.先将表达式放入ArrayList中 //2.将ArrayList传递给一个方法,遍历ArrayList配合栈完成计算* List<String> rpnlist=getListString(suffixExpression);* System.out.println(rpnlist);* */ }//将得到的中缀表达式对应的list-->后缀表达式对应的listpublic static List<String> parseSuffixExpression(List<String> ls){//定义两个栈Stack<String> s1=new Stack<String>();//符号栈//因为s2栈在整个转换过程中,没有pop操作,而且在后续过程中需要逆序输出//直接使用List<String>代替Stack<String>List<String> s2=new ArrayList<String>();//存储中间结果的s2//遍历lsfor(String item:ls) {//如果是一个数,加入s2if(item.matches("\\d+")) {s2.add(item);}else if(item.equals("(")) {s1.push(item);}else if(item.equals(")")) {//如果是右括号,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时这一对括号抛弃while(!s1.peek().equals("(")) {s2.add(s1.pop());}s1.pop();//将左括号弹出,消除小括号}else {//当item的优先级小于等于栈顶,将s1栈顶运算符弹出并加入到s2中,再次转到4.1与s1中新的栈顶运算符相比较while(s1.size()!=0&&Operation.getValue(s1.peek())>=Operation.getValue(item)) {s2.add(s1.pop());}//还需要将item压入栈 s1.push(item);}}//将s1中剩余的运算符弹出并加入到s2while(s1.size()!=0) {s2.add(s1.pop());}return s2;//存放List,因此按顺序输出就是逆波兰表达式 }//将中缀表达式转成对应的Listpublic static List<String> toInfixExpressionList(String s){//定义一个List,存放中缀表达式,对应的内容List<String> ls=new ArrayList<String>();int i=0;//指针,用于遍历中缀表达式字符串String str;//对多位数进行拼接char c;//每遍历到一个字符,就放入到cdo {//如果c是一个非数字,需要加入到ls中去if((c=s.charAt(i))<48||(c=s.charAt(i))>57) {ls.add(""+c);i++;}else {//如果是一个数,需要考虑多位数str="";//先将str置成空串while(i<s.length()&&(c=s.charAt(i))>=48&&(c=s.charAt(i))<=57) {str+=c;//拼接i++;}ls.add(str);}}while(i<s.length());return ls;}//将一个逆波兰表达式,依次将数据和运算符放入到 ArrayListpublic static List<String> getListString(String suffixExpression){//将suffixExpression分割String[] split=suffixExpression.split(" ");List<String> list=new ArrayList<String>();for(String ele:split) {list.add(ele);}return list;}//完成对逆波兰表达式的运算public static int calculate(List<String> ls) {//创建栈,只需要一个栈即可Stack<String> stack=new Stack<String>();//遍历lsfor(String item:ls) {//使用正则表达式取出数if(item.matches("\\d+")) {//匹配的是多位数//入栈 stack.push(item);}else {//pop出两个数,并运算,再入栈int num2=Integer.parseInt(stack.pop());int num1=Integer.parseInt(stack.pop());int res=0;if(item.equals("+")) {res=num1+num2;}else if(item.equals("-")) {res=num1-num2;}else if(item.equals("*")) {res=num1*num2;}else if(item.equals("/")) {res=num1/num2;}else {throw new RuntimeException("运算符有误");}stack.push(res+"");}}//最后留在stack中的数据就是运算结果return Integer.parseInt(stack.pop());} }//编写一个类Operation 可以返回一个运算符对应的优先级 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 operation) {int result=0;switch(operation) {case "+":result=ADD;break;case "-":result=SUB;break;case "*":result=MUL;break;case "/":result=DIV;break;default:System.out.println("不存在该运算符");break; }return result;} }
转载于:https://www.cnblogs.com/ERFishing/p/11251091.html
D3-栈[Java数据结构和算法]相关推荐
- Java数据结构和算法(六)——前缀、中缀、后缀表达式
前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功能吗 ...
- JAVA数据结构与算法【简单介绍】
前几天去面一个大厂,面试官特别好,面试官说到,我们的学习不能本末倒置,数据结构和算法是程序的基础,如果数据结构你没有学好,你真正意义上不算会写代码.你的代码是各处粘贴,杂乱无章的. 由于现在大多用JA ...
- java算法概述,Java数据结构与算法基础(一)概述与线性结构
Java数据结构与算法基础(二)递归算法 Java数据结构与算法基础(一)概述与线性结构 学习目的:为了能更顺畅的读很多底层API代码和拓宽解决问题的思路 一.数据结构概述 1.数据结构是什么?数据与 ...
- Java数据结构和算法(四)--链表
日常开发中,数组和集合使用的很多,而数组的无序插入和删除效率都是偏低的,这点在学习ArrayList源码的时候就知道了,因为需要把要 插入索引后面的所以元素全部后移一位. 而本文会详细讲解链表,可以解 ...
- java数据结构与算法之顺序表与链表深入分析
转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/52953190 出自[zejian的博客] 关联文章: java数据结 ...
- Java数据结构与算法(二)
Java数据结构与算法(二) 第六章 递归 1 递归应用场景 2 递归的概念 3 递归调用机制 4 递归能解决什么样的问题 5 递归需要遵守的重要规则 6 递归-迷宫问题 6.1 迷宫问题 6.2 代 ...
- java数据结构与算法之双链表设计与实现
转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/53047590 出自[zejian的博客] 关联文章: java数据结 ...
- Java 数据结构与算法
目录 Java 数据结构与算法 数据结构 数据结构的定义 数据的逻辑结构 数据的物理结构 数据存储结构 数据结构的分类 线性结构 非线性结构 常用的数据结构 数组(Array) 栈( Stack) 队 ...
- Java 数据结构和算法(十五):无权无向图
Java数据结构和算法(十五)--无权无向图 前面我们介绍了树这种数据结构,树是由n(n>0)个有限节点通过连接它们的边组成一个具有层次关系的集合,把它叫做"树"是因为它看起 ...
- Java数据结构和算法(第二版)
Java数据结构和算法(第二版) 下载地址 https://pan.baidu.com/s/112D5houIgu0eMs_i5o0Ujw 扫码下面二维码关注公众号回复 100066获取分享码 本书目 ...
最新文章
- 2018-4-15摘录笔记,《网络表征学习前沿与实践》 崔鹏以及《网络表征学习中的基本问题初探》 王啸 崔鹏 朱文武
- 每天一个linux命令(29):chgrp命令
- CSS 五种方式实现 Footer 置底
- 成功解决ValueError: Expected 2D array, got 1D array instead: Reshape your data either using array.reshap
- Guava之Ordering
- python爬虫bilibili_Python爬虫 bilibili视频弹幕提取过程详解
- FastDFS文件上传和下载流程
- Python在一个文件夹下遍历得到所有的子文件路径和子文件后缀名
- pythonturtle哪个版本好,python3.7安装turtle(单纯为了好玩)
- php跨域请求post请求失败,nginx + php 跨域问题,GET可以跨域成功,POST失败
- oracle静默安装报错,Oracle静默安装说明
- 预约小程序开发:小程序开发的费用都包含了哪些?
- 胡昊—第6次作业—static关键字、对象
- oracle 的日期 between and 用法
- hapi mysql项目实战路由初始化_hapi框架搭建记录(三):Joi数据校验和Sequelize(数据迁移、填充数据)...
- 英文学习20180529
- 穆斯林的葬礼-(爱情,是人类最美好的感情)
- 【BZOJ1778】[Usaco2010 Hol]Dotp 驱逐猪猡
- /proc 文件系统中的文件及内容介绍
- (一)走进Linux世界(安装Centos8,初始化生产环境,GNU bash)
热门文章
- 关于 iOS 10 中 ATS 的问题
- Hibernate和JDBC、EJB比较
- 45个纯 CSS 实现的精美边框效果【附演示和源码】【上篇】
- java高效代码_Java工程师该如何编写高效代码?
- python交互式命令_在python中运行交互式命令
- lottie 导出html,Lottie Web动效基本原理
- C语言IO流存储数据,C++的IO流
- python的excel库_Python-excel相关库
- java hdfs 新建目录_如何用java在hdfs中创建一个新目录?
- openlayers 根据style设置显示级别并在字体加背景框