一、栈的简介及其基本操作

栈的介绍

1)栈的英文为(stack)

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

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

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

栈的应用场景

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

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

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

4)二叉树的遍历。

5)图形的深度优先(depth - first)搜索法。

数组模拟栈

1)使用数组来模拟栈

2)定义一个 top 来表示栈顶,初始化为 -1

3)入栈的操作,当有数据加入到栈时,top++;stack[top] = data;

4)出栈的操作,int value = stack[top];top--;return value

public class ArrayStackDemo {public static void main(String[] args) {// 测试一下ArrayStack是否正确// 先创建一个ArrayStack对象->表示栈ArrayStack stack = 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: 表示从栈取出数据(出栈)");System.out.println("请输入你的选择");key = scanner.next();switch (key) {case "show":stack.list();break;case "push":System.out.println("请输入一个数");int value = scanner.nextInt();stack.push(value);break;case "pop":try {int res = stack.pop();System.out.printf("出栈的数据是%d\n", res);} catch (Exception e) {System.out.println(e.getMessage());}break;case "exit":scanner.close();loop = false;break;default: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[this.maxSize];}// 栈满public boolean isFull() {return top == maxSize - 1;}// 栈空public boolean isEmpty() {return top == -1;}// 入栈-pushpublic void push(int value) {// 先判断栈是否满if (isFull()) {System.out.println("栈满");return;}top++;stack[top] = value;}// 出栈-pop,将栈顶的数据返回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]);}}
}

栈实现综合计算器【中缀表达式】

1.通过一个index值(索引),来遍历表达式

2.如果发现是一个数字,就直接入数栈

3.如果发现扫描到是一个符号,就分如下情况

3.1 如果发现当前的符号栈为空,就直接入栈

3.2 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符,就需要从数栈中pop出两个数,再从符号栈中pop出一个符号,进行运算,将得到结果,入数栈,然后将当前的操作符入符号栈,如果当前的操作符的优先级大于战中的操作符,就直接入符号栈

4.当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相应的数和符号,并运行。

5.最后在数栈只有一个数字,就是表达式的结果

public class Calculator {public static void main(String[] args) {// 根据前面老师思路,完成表达式的运算String expression = "7*2*2-5+1-5+3-4";// 创建两个栈,数栈,一个符号栈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循环的扫描expressionwhile (true) {// 依次得到expression的每一个字符ch = expression.substring(index, index + 1).charAt(0);// 判断ch是什么,然后做出相应的处理if (operStack.isOper(ch)) {// 如果是运算符// 判断当前的符号栈是否为空if (!(operStack.isEmpty())) {// 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符,就需要从数栈// 中pop出两个数,再从符号栈中pop出一个符号,进行运算,将得到结果,入数栈,然后将当前的操作符// 入符号栈if (operStack.priority(ch) <= operStack.priority(operStack.peek())) {num1 = numStack.pop();num2 = numStack.pop();oper = operStack.pop();res = numStack.cal(num1, num2, oper);// 把运算的结构入数栈numStack.push(res);// 然后把当前的操作符入符号栈operStack.push(ch);} else {// 如果当前的操作符的优先级大于栈中的操作符,就直接入符号栈。operStack.push(ch);}} else {// 如果为空直接入符号栈operStack.push(ch);}} else {// 如果是数,则直接入数栈
//              numStack.push(ch - 48);// 扫描到的表达式是'1',需要转为数字1//这样不能处理多位数,改进// 分析思路// 1.当处理多位数时,不能发现 时一个数就立即入栈,因为它 可能是多位数// 2.在处理数,需要向expression的表达式的index后再看一位,如果是数就进行扫描,如果是符号才入栈// 3.因此需要定义一个变量 字符串,用于拼接// 处理多位数keepNum += ch;// 如果ch已经是expression的最后一位,就直接入栈if (index == expression.length() - 1) {numStack.push(Integer.parseInt(keepNum));} else {// 判断下一个字符是不是数字,如果是数字,就继续扫描,如果是运算符,则入栈// 注意是看后一位,不是index++if (operStack.isOper(expression.substring(index + 1, index + 2).charAt(0))) {// 如果后一位是运算符,则入栈keepNum = "1" 或者 "123"numStack.push(Integer.parseInt(keepNum));// 重要的!!!!,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);// 入栈}// 将数栈的最后数,pop出,就是结果int res2 = numStack.pop();System.out.printf("表达式 %s = %d", expression, res2);}
}
//先创建一个栈,直接使用前面的
//定义一个ArrayStack2表示栈,需要扩展功能
class ArrayStack2 {private int maxSize;// 栈的大小private int[] stack;// 数组,数组模拟栈,数据就放在该数组private int top = -1;// top表示栈顶,初始化为-1// 构造器public ArrayStack2(int maxSize) {this.maxSize = maxSize;stack = new int[this.maxSize];}// 增加一个方法,可以返回当前栈顶的值,但是不是真正的poppublic int peek() {return stack[top];}// 栈满public boolean isFull() {return top == maxSize - 1;}// 栈空public boolean isEmpty() {return top == -1;}// 入栈-pushpublic void push(int value) {// 先判断栈是否满if (isFull()) {System.out.println("栈满");return;}top++;stack[top] = value;}// 出栈-pop,将栈顶的数据返回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 isOper(char val) {return val == '+' || val == '-' || val == '*' || val == '/';}// 计算方法public int cal(int num1, int num2, int oper) {int res = 0;// res用于存放计算的结果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;}
}

二、前缀(波兰表达式)、中缀、后缀表达式(逆波兰表达式)

前缀表达式

1)前缀表达式又称波兰式,前缀表达式的运算符位于操作数之前

2)举例说明:(3 + 4)*5 - 6对应的前缀表达式就是 - * + 3 4 5 6

中缀表达式

后缀表达式

 逆波兰计算器

1)输入一个逆波兰表达式,使用栈(Stack),计算其结果

2)支持小括号和多位数整数

中缀表达式转为后缀表达式

 逆波兰计算器代码:使用到了中缀表达式转后缀表达式

public class PolandNotation {public static void main(String[] args) {// 完成将一个中缀表达式转成后缀表达式的功能// 说明// 1. 1+((2+3)*4)-5 => 1 2 3 + 4 * + 5 -// 2. 因为直接对str进行操作,不方便,因此先将"1+((2+3)*4)-5" => 中缀的表达式对应的List// 即"1+((2+3)*4)-5" => ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]// 3.将得到的中缀表达式对应的List => 后缀表达式对应的List// 即ArrayList[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5] => [1,2,3,+,4,*,+,5,-]String expression = "1+((2+3)*4)-5";List<String> infixExpressionList = toInfixExpressionList(expression);System.out.println(infixExpressionList);// [1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]List<String> suffixExpressionList = parseSuffixExpressionList(infixExpressionList);System.out.println("后缀表达式对应的List" + suffixExpressionList);System.out.printf("expression=%d", calculate(suffixExpressionList));/** // 先定义一个逆波兰表达式 // (3 + 4) * 5 - 6 => 3 4 + 5 * 6 - //* 说明:为了方便,逆波兰表达式的数字和符号使用空格隔开 String suffixExcepression = "3 4 + 5 * 6 -"; // 思路* // 1.先将"3 4 + 5 * 6 - " => 放到ArrayList中 //* 2.将ArrayList传递给一个方法,遍历ArrayList配合栈完成计算 List<String> list =* getListString(suffixExcepression); System.out.println("rpnList=" + list); int* res = calculate(list); System.out.println("计算的结果是=" + res);*/}// 方法:将中缀表达式转成对应的List// s="1+((2+3)*4)-5";public static List<String> toInfixExpressionList(String s) {// 定义一个List,存放中缀表达式对应的内容List<String> ls = new ArrayList<String>();int i = 0;// 这是一个指针,用于遍历中缀表达式字符串String str;// 对多位数的拼接char c;// 每遍历到一个字符,就放入到cdo {// 如果c是一个非数字,就需要加入到lsif ((c = s.charAt(i)) < 48 || (c = s.charAt(i)) < 57) {ls.add("" + c);i++;// i需要后移} else {// 如果是一个数,需要考虑多位数str = "";// 先将str置成"" '0'[48] -> '9'[57]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;// 返回}// 方法:将得到的中缀表达式对应的List => 后缀表达式对应的Listpublic static List<String> parseSuffixExpressionList(List<String> ls) {// 定义两个栈Stack<String> s1 = new Stack<String>();// 符号栈// 说明:因为s2这个栈,在整个转换过程中,没有pop操作,而且后面我们还需要逆序输出// 因此比较麻烦,这里我们就不用Stack<String> 直接使用List<String> s2// Stack<String> s2 = new Stack<String>();//储存有中间结果的栈s2List<String> s2 = new ArrayList<String>();// 储存中间结果的List 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();// !!!!将 ( 弹出s1栈,消除小括号} else {// 当item的优先级小于等于s1栈顶运算符,将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,因此按顺序输出就是对应的后缀表达式对应的List}// 将一个逆波兰表达式,依次将数据和运算符放入到ArrayList中public static List<String> getListString(String suffixExcepression) {// 将suffixExcepression分割String[] split = suffixExcepression.split(" ");List<String> list = new ArrayList<String>();for (String ele : split) {list.add(ele);}return list;}// 完成对逆波兰表达式的运算/** 1)从左至右扫描,将3和4压入堆栈 2)遇到 + 运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈 3)将5入栈* 4)接下来是*运算符,因此弹出5和7,计算出7*5=35,将35入栈 5)将6入栈 6)最后是-运算符,计算出35-6得知,即29,由此得出最终结果*/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出两个数,并运算,再入栈// 注意接收num的数值顺序与后面运算时num的顺序要对应,否则结果会出错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("运算符有误");}// 把res入栈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;}
}

栈、波兰表达式、逆波兰表达式相关推荐

  1. 数据结构-栈应用之逆波兰表达式(后缀表达式)

    逆波兰表达式含义我就不做赘述了,摘自百科上的一段话: 逆波兰表达式又叫做后缀表达式.在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,这种表示法也称为中缀表示.波兰逻辑学家J.Lukas ...

  2. 数据结构:前缀,中缀,后缀表达式(逆波兰表达式)

    前缀表达式(波兰表达式) 前缀表达式的运算符位于操作数之前. 比如 (1+2)*3-4  对应的前缀表达式就是: - * + 1 2 3 4 前缀表达式的计算机求值 从右至左扫描表达式,遇到数字时,就 ...

  3. 数组结构与算法-036-042 前中后缀表达式-逆波兰计算器

    036 前缀 中缀 后缀(逆波兰表达式)表达式 前缀表达式 前缀表达式(波兰表达式) 前缀表达式又称波兰表达式,前缀表达式的运算符位于操作数之前 举例说明:(3 + 4) * 5 -6 对应的前缀表达 ...

  4. 数据结构:后缀表达式(逆波兰表达式)

    逆波兰表达式计算 package com.atchina.stack;import java.util.ArrayList; import java.util.List; import java.ut ...

  5. 四则运算表达式 逆波兰表达式

    感觉是一块比较偏门的东西,特此记录下: 逆波兰表达式: 或者叫后缀表达式,我们原先用的一直都是中缀表达式,但是因为如果表达式过长,计算机计算起来不方便.需要先遍历一边,找出其中的()进行优先计算,如果 ...

  6. java 逆波兰_逆波兰表达式算法-Java版

    这两天一直在看数据结构,栈这个地方,基础的就是这个逆波兰表达式,看了很多博文,都讲得不清不楚或者只能计算一个位的数字,决定自己写,这篇博文给了很大启发–>Go New Land AND Here ...

  7. php 逆波兰 if,逆波兰算法在规则引擎中的运用

    前言 逆波兰表示法(Reverse Polish notation,RPN,或逆波兰记法),是一种是由波兰数学家扬·武卡谢维奇1920年引入的数学表达式方式,在逆波兰记法中,所有操作符置于操作数的后面 ...

  8. 逆波兰表达式[栈 C 语言 实现]

    逆波兰表达式 逆波兰表达式又叫做后缀表达式.在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,这种表示法也称为中缀表示.波兰逻辑学家J.Lukasiewicz于1929年提出了另一种表示 ...

  9. 数据结构:栈的了解与示例(逆波兰表达式)

    数据结构一般需要具备三点 1.能保存数据集合 2.需按照一定规则进行操作 3.可以对数据做各种操作 逆波兰表达式:12 3 + 3 4 - * 表示成普通算术式为 (12+3)*(3-4)=-15 判 ...

  10. 栈的应用之后缀表达式(逆波兰表达式)求值

    要使用栈来实现逆波兰表达式的求值,就得先实现一个栈,并实现栈的一些基本操作: 首先1. 实现栈的如下接口 typedef char DataType; typedef struct Stack { D ...

最新文章

  1. 使用navicat工具创建MySQL存储过程
  2. php-fpm 参数及配置详解
  3. 同花顺怎么导出数据到excel_Excel怎么导出手机通讯录vCard文件
  4. 《编译原理》实验教学大纲
  5. opencv图像连通区域分析
  6. 各种VC9 VC11版本的Apache
  7. 基于JavaSwing开发医院信息管理系统 毕业设计 课程设计 大作业
  8. python编辑器怎么放大字体_增大python字体的方法步骤
  9. 撒娇吧使大家啊让你撒何地什么科的你
  10. 一套策划正确的网页配色方案
  11. App广告投放数据统计
  12. 如何批量调整图片亮度?
  13. Python软件编程等级考试三级——20210314
  14. Python 国内镜像
  15. UCOSII源码分析五——任务优先级与任务调度
  16. 弘辽科技:拼多多搜索排名规则,怎么查?
  17. Hive---Hive函数
  18. VMware共享文件夹设置对,但/mnt/hgfs无共享文件解决方法
  19. 一个页面上有大量的图片(大型电商网站),加载很慢,你有哪些方法优化这些图片的加载,给用户更好的体验。
  20. 《活着》(余华 著)读后感

热门文章

  1. 高斯-克吕格投影与地形图分带
  2. 机器学习——KNN分类器的学习
  3. PHP开发工程师笔试题
  4. openGauss WDR Snapshot生成性能报告详细解读
  5. C# OpenCV EmguCV LSD直线检测使用Demo
  6. itchat 库学习(登录篇)
  7. 【智能硬件】RV1126 CE 板软件指导
  8. CStdioFile总结
  9. 【代码质量】静态代码检测pc-lint, visual lint, cpp-check(pclint、cppcheck)
  10. 平凡而经典的36句人生格言