热门标签 | 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;

 

 

 

 

 


推荐阅读
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文深入探讨了 Java 中的 Serializable 接口,解释了其实现机制、用途及注意事项,帮助开发者更好地理解和使用序列化功能。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 在金融和会计领域,准确无误地填写票据和结算凭证至关重要。这些文件不仅是支付结算和现金收付的重要依据,还直接关系到交易的安全性和准确性。本文介绍了一种使用C语言实现小写金额转换为大写金额的方法,确保数据的标准化和规范化。 ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 深入解析JVM垃圾收集器
    本文基于《深入理解Java虚拟机:JVM高级特性与最佳实践》第二版,详细探讨了JVM中不同类型的垃圾收集器及其工作原理。通过介绍各种垃圾收集器的特性和应用场景,帮助读者更好地理解和优化JVM内存管理。 ... [详细]
  • 本文详细探讨了KMP算法中next数组的构建及其应用,重点分析了未改良和改良后的next数组在字符串匹配中的作用。通过具体实例和代码实现,帮助读者更好地理解KMP算法的核心原理。 ... [详细]
  • 本文详细介绍了 GWT 中 PopupPanel 类的 onKeyDownPreview 方法,提供了多个代码示例及应用场景,帮助开发者更好地理解和使用该方法。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • 本文介绍如何使用Objective-C结合dispatch库进行并发编程,以提高素数计数任务的效率。通过对比纯C代码与引入并发机制后的代码,展示dispatch库的强大功能。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 本文详细介绍了Akka中的BackoffSupervisor机制,探讨其在处理持久化失败和Actor重启时的应用。通过具体示例,展示了如何配置和使用BackoffSupervisor以实现更细粒度的异常处理。 ... [详细]
  • 本文探讨了 Objective-C 中的一些重要语法特性,包括 goto 语句、块(block)的使用、访问修饰符以及属性管理等。通过实例代码和详细解释,帮助开发者更好地理解和应用这些特性。 ... [详细]
  • 本文介绍了如何通过 Maven 依赖引入 SQLiteJDBC 和 HikariCP 包,从而在 Java 应用中高效地连接和操作 SQLite 数据库。文章提供了详细的代码示例,并解释了每个步骤的实现细节。 ... [详细]
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社区 版权所有