热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【Java数据结构和算法】008栈

目录0、警醒自己一、栈的应用场景和介绍1、栈的应用场景一个实际的场景:我的思考:2、栈的介绍入栈演示图:出栈演示图

目录

0、警醒自己

一、栈的应用场景和介绍

1、栈的应用场景

一个实际的场景:

我的思考:

2、栈的介绍

入栈演示图:

出栈演示图:

栈的应用场景:

二、栈的思路分析和代码实现

1、思路分析

数组模拟栈的思路分析图:

2、代码实现

3、运行结果

4、备注

三、栈实现综合计算器

1、思路分析

2、简单的表达式验证思路

简单的表达式:

验证思路:

3、代码实现

4、运行结果

5、关于运算数是多位数的备注




0、警醒自己

1、学习不用心,骗人又骗己;

2、学习不刻苦,纸上画老虎;

3、学习不惜时,终得人耻笑;

4、学习不复习,不如不学习;

5、学习不休息,毁眼伤身体;

7、狗才等着别人喂,狼都是自己寻找食物;

 


一、栈的应用场景和介绍


1、栈的应用场景


一个实际的场景:

请输入一个表达式 计算式:[7*2*2-5+1-5+3-3] 点击计算【如下图】:

请问:计算机底层是如何运算得到结果的?

注意不是简单的把算式列出运算,因为我们看这个算式 7 * 2 * 2 - 5, 但是计算机怎么理解这个算式的(对计算机而言,它接收到的就是一个字符串),我们讨论的是这个问题。-> 栈

 


我的思考:

假如是我的话,给我一串这样的字符串,让我对其进行计算,我的思路是先用正则表达式匹配,看是否存在数组和加减乘除之外的字符,如果有就报错,如果没有就继续计算,因为要先算加减再算乘除,所以先去匹配*/,先获取有多少个数参与计算,也就是+-*/符号的数量+1,要注意相邻的乘除,将乘除计算之后用字符串替换的方式将之前的算式替换为计算的结果,剩下来的就是加和减了,按顺序计算即可,简单思考一下,不成熟的想法,代码实现就不做了,因为我对正则表达式不熟悉。

 


2、栈的介绍

①栈的英文为(stack);

②栈是一个先入后出(FILO-First In Last Out)的有序列表;

③栈(stack)是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表,允许插入和删除的一端,为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom);

④根据栈的定义可知,最先放入栈中元素在栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除;

 


入栈演示图:

 


出栈演示图:

 


栈的应用场景:

①子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中;

②处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数、区域变量等数据存入堆栈中;

③表达式的转换[中缀表达式转后缀表达式]与求值(实际解决);

④二叉树的遍历;

⑤图形的深度优先(depth一first)搜索法;

 


二、栈的思路分析和代码实现


1、思路分析


数组模拟栈的思路分析图:

 


2、代码实现

package com.zb.ds;//演示栈的使用
public class TestStack {public static void main(String[] args) {MyStack stack = new MyStack(5);//入栈stack.push(12);stack.push(71);stack.push(4);stack.push(22);stack.push(99);stack.push(999);//出栈System.out.println(stack.pop() + "出栈成功!");System.out.println(stack.pop() + "出栈成功!");System.out.println(stack.pop() + "出栈成功!");System.out.println(stack.pop() + "出栈成功!");System.out.println(stack.pop() + "出栈成功!");System.out.println(stack.pop() + "出栈成功!");}
}
class MyStack{private int top = -1;private final int[] stack;public MyStack(int length) {stack = new int[length];}//入栈public void push(int num){top++;if(top -1) {return stack[top--];} else {throw new RuntimeException("栈为空,出栈失败!");}}
}

 


3、运行结果

12入栈成功!
71入栈成功!
4入栈成功!
22入栈成功!
99入栈成功!
栈满了,添加失败!
99出栈成功!
22出栈成功!
4出栈成功!
71出栈成功!
12出栈成功!
Exception in thread "main" java.lang.RuntimeException: 栈为空,出栈失败!at com.zb.ds.MyStack.pop(TestStack.java:49)at com.zb.ds.TestStack.main(TestStack.java:20)

 


4、备注

我们也可以写一个判断栈是否满或者为空的方法,在入栈和出栈之前调用判断,这里不再演示;

 


三、栈实现综合计算器


1、思路分析

 


2、简单的表达式验证思路


简单的表达式:

3+2*6-2

 


验证思路:

拿到3,直接入数栈;拿到+,发现符号栈为空,入符号栈;拿到2,直接入数栈;拿到*,发现*的优先级大于符号栈栈顶符号+,入符号栈;拿到6,直接入数栈;拿到-,发现-的优先级小于符号栈栈顶符号*,pop出数栈中的6和2,再pop出符号栈中的*,使得2*6=12,将12直接入数栈,继续比较,发现-的幼年及等于符号栈栈顶符号+,所以再从数栈中pop出12和3,从符号栈中pop出+,使得3+12=15,将15直接入数栈,此时发现符号栈为空,将-直接入符号栈;拿到2,直接如数栈;表达式遍历完毕,从数栈中pop出2和15,从符号栈中pop出-,使得15-2=13,将13入数栈;此时发现,数栈中值的数量为1,符号栈为空,得知数栈中的13就是最终运算结果;

 


3、代码实现

package com.zb.ds;public class MyCalculator {public static void main(String[] args) {// 默认的表达式,咱这里只讨论题目,不讨论二位数字的情况String expression = "7*2*2-5+1-5+3-3";// 创建两个栈:数栈、符号栈MyStack2 numStack = new MyStack2(10);MyStack2 operatorStack = new MyStack2(10);char[] chars = expression.toCharArray();for (char c : chars) {if(MyStack2.isOperator(c)){// 操作符入栈MyStack2.pushOper(c,operatorStack,numStack);}else {// 数字入栈System.out.println("数字入栈:" + c);//字符‘7’等于数字55int cr = Integer.parseInt(c + "");numStack.push(cr);}}// 遍历完毕,全部入栈结束了// 当表达式遍历完毕,就顺序从数栈和符号栈pop出相应的数和符号进行运算,并将结果直接入数栈,以此循环往复直到数栈剩下一个数,// 符号栈为空,此时数栈中的剩下的数就是表达式计算的结果// 其实要循环的次数就是操作符的数量,因为每次运算消耗一个运算符两个数字,又产生一个数字,运算符变化简单int elementNum = operatorStack.getElementNum();while (elementNum!=0){MyStack2.pAndC(operatorStack,numStack);elementNum = operatorStack.getElementNum();}// 遍历完毕,符号栈为空,数栈剩下一个数字,弹出即可得到最终运算结果System.out.println("表达式计算结果:" + numStack.pop());}
}
class MyStack2{private int top &#61; -1;private final int[] stack;private boolean isFull;public MyStack2(int length) {stack &#61; new int[length];}// 入栈public void push(int num){top&#43;&#43;;if(top -1) {return stack[top--];} else {throw new RuntimeException("栈为空&#xff0c;出栈失败&#xff01;");}}// 观察栈顶元素public int watchTop(){if (top > -1) {return stack[top];} else {throw new RuntimeException("栈为空&#xff0c;出栈失败&#xff01;");}}//符号入栈public static void pushOper(char c,MyStack2 operatorStack,MyStack2 numStack){//此时判断一下符号栈是否为空&#xff0c;如果为空&#xff0c;直接入栈if(operatorStack.isNull()){//操作符入栈System.out.println("操作符入栈&#xff1a;" &#43; c);operatorStack.push(c);}else {//进行符号入栈操作//这个时候不应该弹出来&#xff0c;应该是观察int watchTop &#61; operatorStack.watchTop();//比较两个操作符的优先级//分别获取操作符的优先级的数字表示int priorityThis &#61; MyStack2.priority(c);int priorityTop &#61; MyStack2.priority((char) watchTop);// 进行比较&#xff1a;如果当前符号的优先级小于或等于栈顶符号的优先级&#xff0c;那就从数栈中pop两个数&#xff0c;// 从符号栈中pop一个符号&#xff0c;进行运算&#xff0c;将运算结果入数栈if(priorityThis<&#61;priorityTop){int num1 &#61; numStack.pop();int num2 &#61; numStack.pop();int oper &#61; operatorStack.pop();int res &#61; MyStack2.cal(num1, num2, (char) oper);//数字入栈System.out.println("数字入栈&#xff1a;" &#43; res);numStack.push(res);//上一个计算结果入数栈了&#xff0c;那符号应该继续跟栈顶符号进行判断&#xff0c;以此循环&#xff0c;知道当前符号入了符号栈//调用自己&#xff0c;直到c入了符号栈才算罢休pushOper(c,operatorStack,numStack);}else {//操作符入栈System.out.println("操作符入栈&#xff1a;" &#43; c);operatorStack.push(c);}}}//数栈弹出两个数字、符号栈弹出一个符号&#xff0c;进行运算&#xff0c;然后将运算结果入数栈public static void pAndC(MyStack2 operatorStack,MyStack2 numStack){int num1 &#61; numStack.pop();int num2 &#61; numStack.pop();int oper &#61; operatorStack.pop();int res &#61; MyStack2.cal(num1, num2, (char) oper);numStack.push(res);}//获取当前元素数量public int getElementNum(){return top &#43; 1;}//判断是否为空public boolean isNull(){return top &#61;&#61; -1;}//判断是否满public boolean isFull(){return isFull;}//返回运算符的优先级&#xff0c;优先级是程序员来确定的&#xff0c;优先级使用数字表示&#xff0c;我们假定数字越大优先级越高public static int priority(char operator){if(operator&#61;&#61;&#39;*&#39; || operator&#61;&#61;&#39;/&#39;){return 1;}else if(operator&#61;&#61;&#39;&#43;&#39; || operator&#61;&#61;&#39;-&#39;){return 0;}else {return -1;//假定目前表达式只含有&#43;-*/}}//判断是否是一个操作符public static boolean isOperator(char val){return val &#61;&#61; &#39;&#43;&#39; || val &#61;&#61; &#39;-&#39; || val &#61;&#61; &#39;*&#39; || val &#61;&#61; &#39;/&#39;;}//计算方法public static int cal(int num1, int num2, char oper){int res;//res用来存储计算结果switch (oper){case &#39;&#43;&#39;:res &#61; num1 &#43; num2;break;case &#39;-&#39;:res &#61; num2 - num1;break;case &#39;*&#39;:res &#61; num1 * num2;break;case &#39;/&#39;:res &#61; num2 / num1;break;default:throw new RuntimeException("意料之外的情况&#xff0c;操作符为&#xff1a;" &#43; oper);}return res;}
}

 


4、运行结果

数字入栈&#xff1a;7
7入栈成功&#xff01;
操作符入栈&#xff1a;*
42入栈成功&#xff01;
数字入栈&#xff1a;2
2入栈成功&#xff01;
数字入栈&#xff1a;14
14入栈成功&#xff01;
操作符入栈&#xff1a;*
42入栈成功&#xff01;
数字入栈&#xff1a;2
2入栈成功&#xff01;
数字入栈&#xff1a;28
28入栈成功&#xff01;
操作符入栈&#xff1a;-
45入栈成功&#xff01;
数字入栈&#xff1a;5
5入栈成功&#xff01;
数字入栈&#xff1a;23
23入栈成功&#xff01;
操作符入栈&#xff1a;&#43;
43入栈成功&#xff01;
数字入栈&#xff1a;1
1入栈成功&#xff01;
数字入栈&#xff1a;24
24入栈成功&#xff01;
操作符入栈&#xff1a;-
45入栈成功&#xff01;
数字入栈&#xff1a;5
5入栈成功&#xff01;
数字入栈&#xff1a;19
19入栈成功&#xff01;
操作符入栈&#xff1a;&#43;
43入栈成功&#xff01;
数字入栈&#xff1a;3
3入栈成功&#xff01;
数字入栈&#xff1a;22
22入栈成功&#xff01;
操作符入栈&#xff1a;-
45入栈成功&#xff01;
数字入栈&#xff1a;3
3入栈成功&#xff01;
19入栈成功&#xff01;
表达式计算结果&#xff1a;19

 


5、关于运算数是多位数的备注

其实并不难&#xff0c;只需要在发现&#xff08;遍历到&#xff09;一位数字的时候&#xff0c;看看下一位是数字还是符号&#xff0c;是数字继续往后看&#xff0c;知道发现符号&#xff0c;前面的数字是一个数&#xff0c;再用Integer.parseInt()解析成数字即可&#xff1b;

 

 

 

 

 


推荐阅读
  • 快速排序是基于分治策略的一种排序算法,其平均时间复杂度为O(n log n),在大多数情况下表现优于其他排序算法。本文将详细介绍快速排序的工作原理,并提供一个Java语言的具体实现。 ... [详细]
  • 本文详细探讨了 Java 中 Daemon 线程的特点及其应用场景,并深入分析了 Random 类的源代码,帮助开发者更好地理解和使用这些核心组件。 ... [详细]
  • 本文深入探讨了Java注解的基本概念及其在现代Java开发中的应用。文章不仅介绍了如何创建和使用自定义注解,还详细讲解了如何利用反射机制解析注解,以及Java内建注解的使用场景。 ... [详细]
  • 导读上一篇讲了zsh的常用字符串操作,这篇开始讲更为琐碎的转义字符和格式化输出相关内容。包括转义字符、引号、print、printf的使用等等。其中很多内容没有必要记忆,作为手册参 ... [详细]
  • 一个产品数组拼图|集合 2 (O(1)空间) ... [详细]
  • 本文详细介绍了如何在Android游戏中实现360°平滑触屏摇杆,包括摇杆的基本设计原理和具体实现步骤。 ... [详细]
  • Iris 开发环境配置指南 (最新 Go & IntelliJ IDEA & Iris V12)
    本指南详细介绍了如何在最新的 Go 语言环境及 IntelliJ IDEA 中配置 Iris V12 框架,适合初学者和有经验的开发者。文章提供了详细的步骤说明和示例代码,帮助读者快速搭建开发环境。 ... [详细]
  • 深入浅出:Java面向对象编程
    本文详细介绍了Java语言的核心特性——面向对象编程。探讨了Java的基本概念、平台无关性、丰富的内置类库及安全性,同时深入解析了类加载器、垃圾回收机制以及基本数据类型和其包装类。 ... [详细]
  • 立志要引领电视行业趋势的荣耀,最终还是向价格“弯了腰”
    文|佘凯文来源|智能相对论(aixdlun)5月份,“大屏”市场又起风云,各大品牌不约而同地发布了自家新产品。5月26日࿰ ... [详细]
  • 本文详细介绍了 C# 编程语言中 Main 方法的作用、不同形式及其使用场景,帮助开发者更好地理解和应用这一重要概念。 ... [详细]
  • Android json字符串转Map
    Androidjson字符串转Map,Go语言社区,Golang程序员人脉社 ... [详细]
  • Python图像处理库概览
    本文详细介绍了Python中常用的图像处理库,包括scikit-image、Numpy、Scipy、Pillow、OpenCV-Python、SimpleCV、Mahotas、SimpleITK、pgmagick和Pycairo,旨在帮助开发者和研究人员选择合适的工具进行图像处理任务。 ... [详细]
  • 图像中的边缘信息主要集中在高频部分,因此图像锐化或边缘检测实质上是进行高频滤波。微分运算能够增强信号的高频成分,从而在空间域中通过计算微分实现图像锐化。本文将详细介绍如何使用 Python 实现 Canny 边缘检测算法。 ... [详细]
  • 本文探讨如何使用C语言开发一个座位分配系统,包括飞机座位选择、考场座位随机分配等功能,并提供了详细的代码示例。 ... [详细]
  • 十大排序算法JavaScript实现总结
    十大排序算法JavaScript实现总结,Go语言社区,Golang程序员人脉社 ... [详细]
author-avatar
流连的瓶子
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有