Chapter 4 栈

文章目录

  • Chapter 4 栈
    • 4.1 栈
      • 4.1.1 栈的介绍
      • 4.1.2 数组模拟栈
      • 4.1.3 练习:用链表模拟栈
      • 4.1.4 例题:用栈完成表达式计算(中缀表达式)
    • 4.2 前缀、中缀、后缀表达式
      • 4.2.1 前缀表达式(波兰表达式)
        • 4.2.1.1 前缀表达式介绍
        • 4.2.1.2 前缀表达式的计算机求值
      • 4.2.2 中缀表达式
        • 4.2.2.1 中缀表达式介绍
      • 4.2.3 后缀表达式(逆波兰表达式)
        • 4.2.3.1 后缀表达式介绍
        • 4.2.3.2 后缀表达式的计算机求值
        • 4.2.3.3 逆波兰计算器实现
      • 4.2.4 中缀转后缀表达式

4.1 栈

4.1.1 栈的介绍

  • 栈(Stack)是一个先入后出(FILO)的有序列表
  • 栈是限制线性表中元素插入和删除只能在线性表的同一端进行的一种特殊线性表,允许插入和删除的一端为变化的一端,称为栈顶(Top),另一端为固定的一端,称为栈底(Bottom)
  • 根据栈的定义可知,最先放入栈中的元素在栈底,最后放入栈的元素在栈顶,而删除元素刚好相反

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LuAef9bK-1638952873282)(C:/Users/80653/AppData/Roaming/Typora/typora-user-images/image-20211203195112179.png)]

4.1.2 数组模拟栈

思路:

  • 定义一个top指向栈顶,初始化为-1
  • 入栈时 top++;stack[top]=data;
  • 出栈时 int value = stack[top];top–;return value;

实现:

public class ArrayStackTest {public static void main(String[] args) {ArrayStack arrayStack = new ArrayStack(5);arrayStack.push(1);arrayStack.push(2);arrayStack.push(3);arrayStack.push(4);arrayStack.push(5);arrayStack.push(5);arrayStack.listArrayStack();try {arrayStack.pop();arrayStack.pop();arrayStack.pop();arrayStack.pop();arrayStack.pop();}catch (RuntimeException e){System.out.println(e.getMessage());}arrayStack.listArrayStack();}
}class ArrayStack{private int maxSize;private int stack[];private int top = -1;public ArrayStack(int maxSize){this.maxSize = maxSize;stack = new int[maxSize];}//判断栈空public boolean isEmpty(){return top==-1;}//判断栈满public boolean isFull(){return top==maxSize-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 listArrayStack(){if(isEmpty()){System.out.println("栈空,无法遍历");return;}for(int i=top;i>=0;--i){System.out.printf("stack[%d]=%d\n",i,stack[i]);}}
}

4.1.3 练习:用链表模拟栈

import java.util.Stack;public class LinkedListStackTest {public static void main(String[] args) {Data data1 = new Data(1);Data data2 = new Data(2);Data data3 = new Data(3);Data data4 = new Data(4);Data data5 = new Data(5);LinkedListStack linkedListStack = new LinkedListStack();linkedListStack.push(data1);linkedListStack.push(data2);linkedListStack.push(data3);linkedListStack.push(data4);linkedListStack.push(data5);linkedListStack.listLinkedListStack();try {System.out.println(linkedListStack.pop());System.out.println(linkedListStack.pop());System.out.println(linkedListStack.pop());System.out.println(linkedListStack.pop());System.out.println(linkedListStack.pop());}catch (RuntimeException e){System.out.println(e.getMessage());}}
}class LinkedListStack{private Data head = new Data(0);private Data top = head;public LinkedListStack() {}//判断栈空public boolean isEmpty(){return top==head;}//入栈public void push(Data value){Data temp = head;while(temp.next!=null){temp = temp.next;}temp.next = value;top = temp.next;}//出栈public int pop(){if(isEmpty()){throw new RuntimeException("栈空");}Data temp = head;while(temp.next!=top){temp = temp.next;}int value = top.getData();top = temp;temp.next=null;return value;}//遍历栈public void listLinkedListStack(){if (isEmpty()){System.out.println("栈空,无法遍历");return;}Stack<Data> stack = new Stack<>();Data temp = head.next;while(temp!=null){stack.push(temp);temp = temp.next;}while(stack.size()>0){System.out.println(stack.pop());}}
}class Data{private int data;public  Data next;public Data(int data) {this.data = data;}public int getData() {return data;}public void setData(int data) {this.data = data;}@Overridepublic String toString() {return "Data{" +"data=" + data +'}';}
}

4.1.4 例题:用栈完成表达式计算(中缀表达式)

思路:

  1. 通过index来遍历表达式

  2. 如果是一个数字,入数栈

  3. 如果是一个符号

    3.1 如果符号栈中没有符号,就直接入符号栈

    3.2 如果符号栈中有符号

    ​ 3.2.1 如果当前符号的优先级小于或等于栈中的符号,就从数栈中pop出两个数,从符号栈中pop出一个符号,运算后将结果入数栈,最后将当前符号入符号栈

    ​ 3.2.2 如果当前符号的优先级大于栈中的符号,就直接入符号栈

  4. 表达式扫描完毕后,就顺序从数栈和符号栈中pop出相应的数字和符号进行运算

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

实现:

public class Calculator {public static void main(String[] args) {//表达式String expression = "9+2*6-2";//数栈ArrayStack2 numStack = new ArrayStack2(10);//符号栈ArrayStack2 operatorStack = new ArrayStack2(10);int index = 0;int num1=0;int num2=0;int oper=0;int res=0;char ch=' ';while(true){//index获取表达式字符ch = expression.substring(index,index+1).charAt(0);//如果是一个符号if(operatorStack.isOperator(ch)){//判断符号栈中有没有符号//如果有if(!operatorStack.isEmpty()){//判断符号优先级//如果当前符号的优先级小于或等于栈中的符号if(operatorStack.priority(ch)<= operatorStack.priority((char)operatorStack.peek())){//就从数栈中pop出两个数,从符号栈中pop出一个符号,运算后将结果入数栈,最后将当前符号入符号栈num1 = numStack.pop();num2 = numStack.pop();oper = operatorStack.pop();res = numStack.calculate(num1, num2, oper);numStack.push(res);operatorStack.push(ch);}else{//如果当前符号的优先级大于栈中的符号,就直接入符号栈operatorStack.push(ch);}}else{//如果符号栈中没有符号,就直接入符号栈operatorStack.push(ch);}}else{//如果是一个数字,入数栈numStack.push(ch-'0');}index++;if(index==expression.length()){break;}}//表达式扫描完毕后,就顺序从数栈和符号栈中pop出相应的数字和符号进行运算while(true){if(operatorStack.isEmpty()){break;}num1 = numStack.pop();num2 = numStack.pop();oper = operatorStack.pop();res = numStack.calculate(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;public ArrayStack2(int maxSize){this.maxSize = maxSize;stack = new int[maxSize];}//获取栈顶信息public int peek(){return stack[top];}//判断栈空public boolean isEmpty(){return top==-1;}//判断栈满public boolean isFull(){return top==maxSize-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 listArrayStack(){if(isEmpty()){System.out.println("栈空,无法遍历");return;}for(int i=top;i>=0;--i){System.out.printf("stack[%d]=%d\n",i,stack[i]);}}//判断是否为符号public boolean isOperator(char ch){return ch=='+'||ch=='-'||ch=='*'||ch=='/';}//判断符号优先级public int priority(char ch){if(ch=='*'||ch=='/'){return 1;}else if(ch=='+'||ch=='-'){return 0;}else{return -1;}}//计算public int calculate(int num1,int num2,int operator){int res = 0;switch (operator){case '+':res = num1+num2;break;case '-':res = num2-num1;break;case '*':res = num1*num2;break;case '/':res = num2/num1;default:break;}return res;}
}

存在问题:多位数计算的时候结果不正确

//优化解决问题
public static void main(String[] args) {//表达式String expression = "90+20*6-2";//数栈ArrayStack2 numStack = new ArrayStack2(10);//符号栈ArrayStack2 operatorStack = new ArrayStack2(10);int index = 0;int num1=0;int num2=0;int oper=0;int res=0;char ch=' ';String connChar = "";//用于拼接数字while(true){//index获取表达式字符ch = expression.substring(index,index+1).charAt(0);//如果是一个符号if(operatorStack.isOperator(ch)){//判断符号栈中有没有符号//如果有if(!operatorStack.isEmpty()){//判断符号优先级//如果当前符号的优先级小于或等于栈中的符号if(operatorStack.priority(ch)<= operatorStack.priority((char)operatorStack.peek())){//就从数栈中pop出两个数,从符号栈中pop出一个符号,运算后将结果入数栈,最后将当前符号入符号栈num1 = numStack.pop();num2 = numStack.pop();oper = operatorStack.pop();res = numStack.calculate(num1, num2, oper);numStack.push(res);operatorStack.push(ch);}else{//如果当前符号的优先级大于栈中的符号,就直接入符号栈operatorStack.push(ch);}}else{//如果符号栈中没有符号,就直接入符号栈operatorStack.push(ch);}}else{/*如果是一个数字,入数栈numStack.push(ch-'0');*///如果是数字,先拼接到connCharconnChar+=ch;//如果ch已经是最后一个了,直接入数栈if(index==expression.length()-1){numStack.push(Integer.valueOf(connChar));}else{//如果不是最后一个//扫描下一个字符//如果是运算符,则将connChar直接入数栈if(operatorStack.isOperator(expression.substring(index+1, index+2).charAt(0))){numStack.push(Integer.valueOf(connChar));//清空connCharconnChar="";}}}index++;if(index==expression.length()){break;}}//表达式扫描完毕后,就顺序从数栈和符号栈中pop出相应的数字和符号进行运算while(true){if(operatorStack.isEmpty()){break;}num1 = numStack.pop();num2 = numStack.pop();oper = operatorStack.pop();res = numStack.calculate(num1, num2, oper);numStack.push(res);}//最后的数栈中只有一个数字,就是表达式的结果System.out.printf("表达式%s的结果是%d",expression,numStack.pop());
}

4.2 前缀、中缀、后缀表达式

4.2.1 前缀表达式(波兰表达式)

4.2.1.1 前缀表达式介绍

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

例如:(3+4)*5-6 的前缀表达式为 - * + 3 4 5 6

4.2.1.2 前缀表达式的计算机求值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TBtgHSCu-1638952873284)(C:/Users/80653/AppData/Roaming/Typora/typora-user-images/image-20211205161338736.png)]

4.2.2 中缀表达式

4.2.2.1 中缀表达式介绍

中缀表达式就是最常见的运算表达式,例如:(3+4)*5-6

中缀表达式求值对计算机来说不好操作,往往将中缀表达式转为其他表达式来操作(常常转为后缀表达式)

4.2.3 后缀表达式(逆波兰表达式)

4.2.3.1 后缀表达式介绍

后缀表达式又称为逆波兰表达式,后缀表达式的运算符位于操作数之后

例如:(3+4)*5-6的后缀表达式为 3 4 + 5 * 6 -

4.2.3.2 后缀表达式的计算机求值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ieQPhsF5-1638952873285)(C:/Users/80653/AppData/Roaming/Typora/typora-user-images/image-20211205162350553.png)]

4.2.3.3 逆波兰计算器实现

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;public class PolandNotation {public static void main(String[] args) {//定义一个逆波兰表达式String suffixExpression = "3 4 + 5 * 6 -";//将suffixExpression放到ArrayList中List<String> expressionList = getListString(suffixExpression);System.out.printf("逆波兰表达式\"%s\"的结果是%d",suffixExpression,calculate(expressionList));}//将suffixExpression放到ArrayList中public static List<String> getListString(String suffixExpression){//按空格分割,放入String数组String[] splits = suffixExpression.split(" ");List<String> expressionList = new ArrayList<>();//将String数组中的元素依次放入expressionListfor(String element:splits){expressionList.add(element);}return expressionList;}//传入expressionList,结合栈计算结果public static int calculate(List<String> expressionList){Stack<String> stack = new Stack<>();for(String item:expressionList){if(item.matches("\\d+")){stack.push(item);}else{int num2= Integer.valueOf(stack.pop());int num1= Integer.valueOf(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{res = num1 / num2;}stack.push(Integer.toString(res));}}return Integer.valueOf(stack.pop());}}

4.2.4 中缀转后缀表达式

思路:

  1. 初始化两个栈,一个存放符号的符号栈s1,一个存储中间结果的临时栈s2

  2. 从左至右扫描表达式

  3. 遇到操作数,直接压入s2

  4. 遇到运算符时,比较其与s1栈顶运算符的优先级;

    4.1 如果s1为空,或者s1栈顶为‘(’,则直接入符号栈s1

    4.2 否则,如果运算符的优先级大于栈顶运算符的优先级,也直接压入符号栈s1

    4.3 否则,将s1栈顶的运算符弹出并压入到s2中,然后再回到 4.1 与s1中新的栈顶运算符比较

  5. 遇到括号时

    5.1 如果是‘(’,则直接压入s1

    5.2 如果是’)’,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号,最后需要丢弃这一对括号

  6. 重复2到5步,直到表达式扫描完毕

  7. 将s1中的符号依次弹出压入到s2

  8. 依次弹出s2中的元素并输出,结果的逆序就是中缀表达式对应的后缀表达式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UAkPTSq3-1638952873286)(C:/Users/80653/AppData/Roaming/Typora/typora-user-images/image-20211207154559720.png)]

实现:

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;public class PolandNotation {public static void main(String[] args) {String infixExpression = "1+((2+3)*4)-5";//将infixExpression放入到list方便后面的操作List<String> list = new ArrayList<>();list = toInfixExpressionList(infixExpression);System.out.printf("中缀表达式%s对应的后缀表达式是:%s\n",infixExpression,parseSuffixExpressionList(list));System.out.printf("表达式%s的结果是:%d\n",infixExpression,calculate(parseSuffixExpressionList(list)));}//将中缀表达式转为后缀表达式public static List<String> parseSuffixExpressionList(List<String> list){Stack<String> s1 = new Stack<>();List<String> s2 = new ArrayList<>();//s2没有出栈操作,而且s2栈的结果需要逆序,所以使用List更方便for(String str : list){//如果遇到操作数,直接加入s2if (str.matches("\\d+")){s2.add(str);} else if(str.equals("(")){//如果遇到"(",直接压入s1s1.push(str);} else if(str.equals(")")){//如果是')',则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号,最后需要丢弃这一对括号while(!s1.peek().equals("(")){s2.add(s1.pop());}s1.pop();} else {while(s1.size()!=0 && getPriority(s1.peek()) >= getPriority(str)){s2.add(s1.pop());}s1.push(str);}}while(s1.size()!=0){s2.add(s1.pop());}return s2;}//判断符号优先级public static int getPriority(String operator){int res = 0;switch (operator){case "+":case "-":res = 1;break;case "*":case "/":res = 2;break;default:break;}return res;}//将infixExpression放入到listpublic static List<String> toInfixExpressionList(String infixExpression){List<String> list = new ArrayList<>();//用于存放结果int index = 0;//用于遍历infixExpressionchar ch = ' ';//用于获取当前字符String temp = "";//用于拼接多位数int length = infixExpression.length();while(index<length){ch = infixExpression.charAt(index);if(ch<'0'||ch>'9'){list.add(ch+"");index++;}else{temp = "";while(ch>='0'&&ch<='9'){temp+=ch;index++;if(index<length) {ch = infixExpression.charAt(index);}else{break;}}list.add(temp);}}return list;}//将suffixExpression放到ArrayList中public static List<String> getListString(String suffixExpression){//按空格分割,放入String数组String[] splits = suffixExpression.split(" ");List<String> expressionList = new ArrayList<>();//将String数组中的元素依次放入expressionListfor(String element:splits){expressionList.add(element);}return expressionList;}//传入expressionList,结合栈计算结果public static int calculate(List<String> expressionList){Stack<String> stack = new Stack<>();for(String item:expressionList){if(item.matches("\\d+")){stack.push(item);}else{int num2= Integer.valueOf(stack.pop());int num1= Integer.valueOf(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{res = num1 / num2;}stack.push(Integer.toString(res));}}return Integer.valueOf(stack.pop());}}

【自学笔记】尚硅谷数据结构与算法Chapter 4 栈相关推荐

  1. 【自学笔记】尚硅谷数据结构与算法Chapter 1 数据结构与算法概述

    Chapter 1 数据结构与算法概述 文章目录 Chapter 1 数据结构与算法概述 1.1.1 数据结构和算法的关系 1.2.1 线性结构 1.2.2 非线性结构 尚硅谷数据结构B站学习视频地址 ...

  2. 【自学笔记】尚硅谷数据结构与算法Chapter 3 链表

    Chapter 3 链表 文章目录 Chapter 3 链表 3.1 链表 3.1.1 链表介绍 3.1.2 链表CRUD 3.1.3 单链表题目 3.2 双向链表 3.2.1 双向链表介绍 3.2. ...

  3. 【自学笔记】尚硅谷数据结构与算法Chapter 5 递归

    Chapter 5 递归 文章目录 Chapter 5 递归 5.1 递归概念 5.2 递归的调用机制 5.3 迷宫问题(回溯) 5.4 八皇后问题(回溯) 5.1 递归概念 递归好就是方法自己调用自 ...

  4. 【自学笔记】尚硅谷数据结构与算法Chapter 2 稀疏数组和队列

    Chapter 2 稀疏数组和队列 文章目录 Chapter 2 稀疏数组和队列 2.1 稀疏数组 2.1.1 基本介绍 2.1.2 应用案例 2.2 队列 2.2.1 队列介绍 2.2.2 用数组模 ...

  5. 尚硅谷数据结构与算法(Java)--17--归并排序

    :归并排序 归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 将已有序的子序列合并,得到完全有序的序列:即先使每个 ...

  6. 尚硅谷数据结构和算法01-数据结构介绍和稀疏数组

    文章目录 一.数据结构组成 1.线性结构 2.非线性结构 二.稀疏数组 1.应用场景: 2.稀疏数组转换的思路分析 3.代码实现 补充: %d\t 一.数据结构组成 包括:线性结构和非线性结构 1.线 ...

  7. 电商数仓描述_笔记-尚硅谷大数据项目数据仓库-电商数仓V1.2新版

    架构 项目框架 数仓架构 存储压缩 Snappy与LZO LZO安装: 读取LZO文件时,需要先创建索引,才可以进行切片. 框架版本选型Apache:运维麻烦,需要自己调研兼容性. CDH:国内使用最 ...

  8. 【笔记】Java数据结构与算法

    [笔记]Java数据结构与算法 文章目录 [笔记]Java数据结构与算法 1.八大排序应用场景 2.未完待续-- 1.八大排序应用场景 冒泡排序:优化后的冒泡排序可用于当数据已经基本有序,且数据量较小 ...

  9. MySQL学习笔记——尚硅谷李玉婷经典版MySQL基础笔记(一)

    MySQL学习笔记--尚硅谷李玉婷经典版MySQL基础笔记(一) MySQL学习笔记目录 MySQL学习笔记--尚硅谷李玉婷经典版MySQL基础笔记(一) 一.基础知识 1.MySQL的语法规范 2. ...

最新文章

  1. new 数组_编程-遍历数组元素N次/填充数组至指定长度
  2. Python Django jsonpickle序列化部分字段
  3. 网络协议之:一定要大写的SOCKS
  4. 《UNIX网络编程卷1》第一例及问题
  5. 【我的物联网成长记1】如何进行端到端开发?
  6. MyBatis的概述及使用
  7. Html前端基础(select下拉选标签、table表格标签)
  8. linux进程及作业管理实验,Linux 进程及作业管理(一)
  9. SQL-92标准 中文翻译——定义、记号和约定 (记号)
  10. Leo-io 的C语言实现简单五子棋游戏观后感
  11. 华米OV 万亿 IoT 争夺战
  12. 前端开发的壁垒在哪儿?
  13. matlab仿真卫星导航接收机信号干扰,卫星导航软件接收机多采样率信号处理技术...
  14. ts 视频流手动合并
  15. 前端工作七个月经验总结以及技术分享
  16. Xshell下载文件到本地
  17. 基因功能分析——哈佛大学
  18. Jekyll分页功能
  19. scrapy 简单教程
  20. python万年历实验报告_Python编程——万年历

热门文章

  1. 美国名校的网上竞争雷人语录
  2. 【图像融合】像素点图像融合【含GUI Matlab源码 783期】
  3. 360校园招聘2015技术类笔试题
  4. 同或门真值表_逻辑函数表达式、真值表.ppt
  5. 大物计算机在线使用,计算机怎么发展为电脑的
  6. Ubuntu 下挂ISO到虚拟光驱的方法
  7. 手绘线条一直画不直_手绘板画出的斜直线不直,总是波浪线。
  8. 送5本刚刚出版的《大数据时代生存法则》
  9. R在市场调查中的应用--主成分分析
  10. oracle中文转换为unicode,oracle 用函数unistr将Oracle数据库中的Unicode转换为中文