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

 

 

 

 

 


推荐阅读
  • 本文详细记录了一位求职者在搜狐进行的两次面试经历,包括面试的具体时间、面试流程、技术问题及个人感受。通过本次面试,作者不仅获得了宝贵的经验,还成功拿到了搜狐的录用通知。 ... [详细]
  • 深入解析链表成环问题:剑指Offer第22天的新视角
    本文将详细介绍链表成环问题的多种解法,包括哈希表法、JSON.stringify特殊解法及双指针法,并提供详尽的代码示例。阅读本文,你不仅能够掌握这一经典算法问题的核心技巧,还能了解到更多编程思维的拓展。 ... [详细]
  • Java高级工程师学习路径及面试准备指南
    本文基于一位朋友的PDF面试经验整理,涵盖了Java高级工程师所需掌握的核心知识点,包括数据结构与算法、计算机网络、数据库、操作系统等多个方面,并提供了详细的参考资料和学习建议。 ... [详细]
  • 我的读书清单(持续更新)201705311.《一千零一夜》2006(四五年级)2.《中华上下五千年》2008(初一)3.《鲁滨孙漂流记》2008(初二)4.《钢铁是怎样炼成的》20 ... [详细]
  • 本文总结了一次针对大厂Java研发岗位的面试经历,探讨了面试中常见的问题及其背后的原因,并分享了一些实用的面试准备资料。 ... [详细]
  • 本文深入探讨了JLine库中的ConsoleReader.drawBuffer()方法的使用场景和具体实现,通过多个实际代码示例,帮助开发者更好地理解和应用此方法。 ... [详细]
  • MVC框架下使用DataGrid实现时间筛选与枚举填充
    本文介绍如何在ASP.NET MVC项目中利用DataGrid组件增强搜索功能,具体包括使用jQuery UI的DatePicker插件添加时间筛选条件,并通过枚举数据填充下拉列表。 ... [详细]
  • 本文探讨了Web API 2中特性的路由机制,特别是如何利用它来构建RESTful风格的URI。文章不仅介绍了基本的特性路由使用方法,还详细说明了如何通过特性路由进行API版本控制、HTTP方法的指定、路由前缀的应用以及路由约束的设置。 ... [详细]
  • 择要:Fundebug的JavaScript毛病监控插件同步支撑Vue.js异步毛病监控。Vue.js从降生至今已5年,尤大在本年2月份宣布了严重更新,即Vue2.6。更新包含新增 ... [详细]
  • 本文详细记录了一位Java程序员在Lazada的面试经历,涵盖同步机制、JVM调优、Redis应用、线程池配置、Spring框架特性等多个技术点,以及高级面试中的设计问题和解决方案。 ... [详细]
  • 本文介绍了如何利用Java编程语言中的正则表达式来验证字符串中的数字是否符合中国三大运营商(中国电信、中国联通、中国移动)的手机号码格式。文章提供了详细的代码示例和解析。 ... [详细]
  • 如何高效学习鸿蒙操作系统:开发者指南
    本文探讨了开发者如何更有效地学习鸿蒙操作系统,提供了来自行业专家的建议,包括系统化学习方法、职业规划建议以及具体的开发技巧。 ... [详细]
  • Java虚拟机及其发展历程
    Java虚拟机(JVM)是每个Java开发者日常工作中不可或缺的一部分,但其背后的运作机制却往往显得神秘莫测。本文将探讨Java及其虚拟机的发展历程,帮助读者深入了解这一关键技术。 ... [详细]
  • 默认情况下,Java 的克隆机制是浅克隆,即仅复制对象本身而不复制其内部引用的对象。本文将详细介绍如何通过深度克隆来确保对象及其内部引用的对象都能被正确复制。 ... [详细]
  • Java中字符串截取方法详解
    本文详细介绍了Java中常用的字符串截取方法及其应用场景,帮助开发者更好地理解和使用这些方法。 ... [详细]
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社区 版权所有