前言

数据 data 结构(structure)是一门 研究组织数据方式的学科,有了编程语言也就有了数据结构.学好数据结构才可以编写出更加漂亮,更加有效率的代码。

  • 要学习好数据结构就要多多考虑如何将生活中遇到的问题,用程序去实现解决.
  • 程序 = 数据结构 + 算法
  • 数据结构是算法的基础, 换言之,想要学好算法,需要把数据结构学到位

我会用数据结构与算法【Java】这一系列的博客记录自己的学习过程,如有遗留和错误欢迎大家提出,我会第一时间改正!!!

注:数据结构与算法【Java】这一系列的博客参考于B站尚硅谷的视频,视频原地址为【尚硅谷】数据结构与算法(Java数据结构与算法)

上一篇文章数据结构与算法【Java】02---链表

接下来进入正题!

1、栈的简介

  1. 栈的英文为(stack)
  2. 栈是一个 先入后出(FILO-First In Last Out) 的有序列表。
  3. 栈(stack) 是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为 变化的一端,称为栈顶(Top) ,另一端为 固定的一端,称为栈底(Bottom) 。
  4. 根据栈的定义可知 , 最先放入栈中元素在栈底 , 最后放入的元素在栈顶 , 而删除元素刚好相反 , 最后放入的元素最先删除,最先放入的元素最后删除
  5. 图解方式说明出栈(pop) 和入栈(push)
  • 入栈

  • 出栈

2、栈的应用场景

  1. 子程序的调用:在跳往子程序前,会先将下个指令的地址存到堆栈中,直到子程序执行完后再将地址取出,以回到原来的程序中。
  2. 处理递归调用:和子程序的调用类似,只是除了储存下一个指令的地址外,也将参数、区域变量等数据存入堆栈中。
  3. 表达式的转换[中缀表达式转后缀表达式]与求值(实际解决)。
  4. 二叉树的遍历。
  5. 图形的深度优先(depth 一 first)搜索法。

3、栈的快速入门

数组模拟栈

  • 思路分析

  • 代码实现

    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 "exit":
    scanner.close();
    loop = false;
    break;
    case "push":
    System.out.println("请输入一个数");
    int value = scanner.nextInt();
    stack.push(value);
    break;
    case "pop":
    try {
    int res = stack.pop();
    System.out.println("出栈的数据是:"+res);
    }catch (Exception e){
    System.out.println(e.getMessage());
    }
    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;//数组下标从0开始,栈的最后一个下标是maxSize - 1
    } //判断栈空
    public boolean isEmpty(){
    return top == -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 list(){
    if(isEmpty()){
    System.out.println("栈空");
    return;
    }
    for (int i = top; i >= 0; i--) {
    System.out.printf("stack[%d]=%d\n",i,stack[i]);
    }
    } }
  • 结果展示

4、中缀表达式计算

  • 需求分析

    • 计算中缀表达式:7*1+6/3-4
  • 思路分析

  • 代码实现

     public class Calculator {
    public static void main(String[] args) {
    //测试表达式的运算
    String expression = "7*1+6/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保存到ch
    String keepNum = "";//用于拼接多位数
    //用while循环扫描expression
    while (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);//-48 数字对应ascii码表
    //分析思路
    //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))) {
    //如果后一位是运算符就入栈
    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); }
    } //先创建一个栈,这里直接使用前面创建好的
    //定义一个ArrayStack表示栈,需要扩展功能
    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];
    } //增加一个方法,可以返回当前栈顶的值, 但是不是出栈
    public int peek() {
    return stack[top];
    } //判断栈满
    public boolean isFull(){
    return top == maxSize - 1;//数组下标从0开始,栈的最后一个下标是maxSize - 1
    } //判断栈空
    public boolean isEmpty(){
    return top == -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 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;//用于存放计算的结果
    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) 处理多位数

5、逆波兰计算器

  • 需求分析

    • 输入一个逆波兰表达式(后缀表达式),使用栈(Stack), 计算其结果
    • 支持小括号和多位数整数
    • 可以用 30 4 + 5 * 6 -举例
  • 思路分析

    • 例如: (3+4)5-6 对应的后缀表达式就是 3 4 + 5 * 6 - , 针对后缀表达式求值步骤如下:

      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 class PolandNotation {
    public static void main(String[] args) { //先定义一个逆波兰表达式
    //(30 + 4) * 5 - 6 -> 30 4 + 5 * 6 -
    //为了方便,逆波兰表达式的数字和符号用空格隔开
    String suffixExpression ="30 4 + 5 * 6 -";
    //思路
    //1.先将suffixExpression放到ArrayList中
    //2.将ArrayList传递给一个方法 ,配合栈完成计算 List<String> rpnList = getListString(suffixExpression);
    System.out.println("rpnList="+rpnList); int res = calculate(rpnList);
    System.out.println("计算的结果是:"+res); } //将一个逆波兰表达式,依次将数据和运算符放入到ArrayList中
    public static List<String> getListString(String suffixExpression){
    //将suffixExpression分割
    String[] split = suffixExpression.split(" ");
    ArrayList<String> list = new ArrayList<>();
    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<>();
    //遍历 ls
    for (String item:ls) {
    //这里使用正则表达式来取数
    if(item.matches("\\d+")){//匹配的是多位数
    //入栈
    stack.push(item);
    }else {
    //pop出两个数并运算,再入栈
    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());
    } }
  • 结果展示

6、中缀表达式转换为后缀表达式

通过上面逆波兰计算器的例子我们发现,后缀表达式适合计算机进行计算,但是我们却不太容易写出来,所以接下来我们需要将中缀表达式转换为后缀表达式

中缀表达式转换为后缀表达式的一般步骤是

1.初始化两个栈:运算符栈 s1 和储存中间结果的栈 s2;

2.从左至右扫描中缀表达式;

3.遇到操作数时,将其压 s2;

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

1.如果 s1 为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;

2.否则,若优先级比栈顶运算符的高,也将运算符压入 s1;

3.否则,将 s1 栈顶的运算符弹出并压入到 s2 中,再次转到(4-1)与 s1 中新的栈顶运算符相比较;

5.遇到括号时:

(1) 如果是左括号“(”,则直接压入 s1

(2) 如果是右括号“)”,则依次弹出 s1 栈顶的运算符,并压入 s2,直到遇到左括号为止,此时将这一对括号丢弃

6.重复步骤 2 至 5,直到表达式的最右边

7.将 s1 中剩余的运算符依次弹出并压入 s2

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

  • 思路分析(重点)

  • 代码实现

    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] =》 ArrayList [1,2,3,+,4,*,+,5,–]
    String expression = "1+((2+3)*4)-5";
    List<String> infixExpression = toInfixExpression(expression);
    System.out.println("中缀表达式对应的List="+infixExpression); List<String> strings = parseSuffixExpressionList(infixExpression);
    System.out.println("后缀表达式对应的List="+strings); System.out.println("expression="+calculate(strings)); } //即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] =》 ArrayList [1,2,3,+,4,*,+,5,–]
    //方法:将得到的中缀表达式对应的List => 后缀表达式对应的List
    public 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>(); // 储存中间结果的栈s2
    List<String> s2 = new ArrayList<String>(); // 储存中间结果的Lists2 //遍历ls
    for (String item:ls ) {
    //如果是一个数就加入到s2
    if(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中剩余的运算符依次弹出并加入到s2中
    while (s1.size()!=0){
    s2.add(s1.pop());
    }
    //注意因为是存放到List, 因此按顺序输出就是对应的后缀表达式对应的List
    return s2;
    } //将中缀表达式转成对应的List
    public static List<String> toInfixExpression(String s){
    //定义一个List,存放中缀表达式对应的内容
    List<String> ls = new ArrayList<>();
    int i = 0;//用于遍历中缀表达式的字符串,指针
    String str;//对多位数的拼接
    char c;//每遍历到一个字符,就放入到c中
    do {
    //如果c是一个非数字就需要加入到ls中
    if((c = s.charAt(i)) < 48 || (c=s.charAt(i)) >57){
    ls.add(""+c);
    i++;
    } else {//如果是一个数,需要考虑多位数的问题
    str = "";//先将str置成空串
    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;
    } //将一个逆波兰表达式,依次将数据和运算符放入到ArrayList中
    public static List<String> getListString(String suffixExpression){
    //将suffixExpression分割
    String[] split = suffixExpression.split(" ");
    ArrayList<String> list = new ArrayList<>();
    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<>();
    //遍历 ls
    for (String item:ls) {
    //这里使用正则表达式来取数
    if(item.matches("\\d+")){//匹配的是多位数
    //入栈
    stack.push(item);
    }else {
    //pop出两个数并运算,再入栈
    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("不存在该运算符:"+operation);
    break;
    }
    return result;
    }
    }
  • 结果展示

7、完整版逆波兰计算器

  • 功能描述

    1. 支持 + - * / ( )
    2. 多位数,支持小数,
    3. 兼容处理, 过滤任何空白字符,包括空格、制表符、换页符
  • 代码实现

    package com.qjd.stack;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Stack;
    import java.util.regex.Pattern; public class ReversePolishMultiCalc {
    /**
    * 匹配 + - * / ( ) 运算符
    */
    static final String SYMBOL = "\\+|-|\\*|/|\\(|\\)";
    static final String LEFT = "(";
    static final String RIGHT = ")";
    static final String ADD ="+";
    static final String MINUS = "-";
    static final String TIMES = "*";
    static final String DIVISION = "/";
    /**
    * 加減 + -
    */
    static final int LEVEL_01 = 1; /**
    * 乘除 * /
    */
    static final int LEVEL_02 = 2;
    /**
    * 括号
    */
    static final int LEVEL_HIGH = Integer.MAX_VALUE;
    static Stack<String> stack = new Stack<>();
    static List<String> data = Collections.synchronizedList(new ArrayList<String>()); /**
    * 去除所有空白符
    *
    * @param s
    * @return
    */
    public static String replaceAllBlank(String s) {
    // \\s+ 匹配任何空白字符,包括空格、制表符、换页符等等, 等价于[ \f\n\r\t\v]
    return s.replaceAll("\\s+", "");
    } /**
    * 判断是不是数字 int double long float
    * 尚硅谷 Java 数据结构和算法
    * 更多 Java –大数据 –前端 –python 人工智能 -区块链资料下载,可访问百度:尚硅谷官网
    * 第 94页
    *
    * @param s
    * @return
    */
    public static boolean isNumber(String s) {
    Pattern pattern = Pattern.compile("^[-\\+]?[.\\d]*$");
    return pattern.matcher(s).matches();
    } /**
    * 判断是不是运算符
    *
    * @param s
    * @return
    */
    public static boolean isSymbol(String s) {
    return s.matches(SYMBOL);
    } /**
    * 匹配运算等级
    *
    * @param s
    * @return
    */
    public static int calcLevel(String s) {
    if ("+".equals(s) || "-".equals(s)) {
    return LEVEL_01;
    } else if ("*".equals(s) || "/".equals(s)) { return LEVEL_02;
    }
    return LEVEL_HIGH;
    } /**
    * 匹配
    *
    * @param s
    * @throws Exception
    */
    public static List<String> doMatch(String s) throws Exception {
    if (s == null || "".equals(s.trim())) throw new RuntimeException("data is empty");
    if (!isNumber(s.charAt(0) + "")) throw new RuntimeException("data illeagle,start not with a number");
    s = replaceAllBlank(s);
    String each;
    int start = 0;
    for (int i = 0; i < s.length(); i++) {
    if (isSymbol(s.charAt(i) + "")) {
    each = s.charAt(i) + "";
    //栈为空,(操作符,或者 操作符优先级大于栈顶优先级 && 操作符优先级不是( )的优先级 及是 )不能直接入栈
    if (stack.isEmpty() || LEFT.equals(each)
    || ((calcLevel(each) > calcLevel(stack.peek())) && calcLevel(each) < LEVEL_HIGH)) { stack.push(each);
    } else if (!stack.isEmpty() && calcLevel(each) <= calcLevel(stack.peek())) {
    //栈非空,操作符优先级小于等于栈顶优先级时出栈入列,直到栈为空,或者遇到了(,最后操作符入栈
    while (!stack.isEmpty() && calcLevel(each) <= calcLevel(stack.peek())) {
    if (calcLevel(stack.peek()) == LEVEL_HIGH) {
    break;
    }
    data.add(stack.pop());
    }
    stack.push(each);
    } else if (RIGHT.equals(each)) {
    // ) 操作符,依次出栈入列直到空栈或者遇到了第一个)操作符,此时)出栈
    while (!stack.isEmpty() && LEVEL_HIGH >= calcLevel(stack.peek())) {
    if (LEVEL_HIGH == calcLevel(stack.peek())) {
    stack.pop();
    break;
    }
    data.add(stack.pop());
    }
    }
    start = i; //前一个运算符的位置
    } else if (i == s.length() - 1 || isSymbol(s.charAt(i + 1) + "")) {
    each = start == 0 ? s.substring(start, i + 1) : s.substring(start + 1, i + 1);
    if (isNumber(each)) {
    data.add(each); continue;
    }
    throw new RuntimeException("data not match number");
    }
    }
    //如果栈里还有元素,此时元素需要依次出栈入列,可以想象栈里剩下栈顶为/,栈底为+,应该依次出栈入列,可以直接翻转整个 stack 添加到队列
    Collections.reverse(stack);
    data.addAll(new ArrayList < > (stack));
    System.out.println(data);
    return data;
    } /**
    * 算出结果
    *
    * @param list
    * @return
    */
    public static Double doCalc(List<String> list) {
    Double d = 0d;
    if (list == null || list.isEmpty()) {
    return null;
    }
    if (list.size() == 1) {
    System.out.println(list); d = Double.valueOf(list.get(0));
    return d;
    }
    ArrayList<String> list1 = new ArrayList <>();
    for (int i = 0; i < list.size(); i++) {
    list1.add(list.get(i));
    if (isSymbol(list.get(i))) {
    Double d1 = doTheMath(list.get(i - 2), list.get(i - 1), list.get(i));
    list1.remove(i);
    list1.remove(i - 1);
    list1.set(i - 2, d1 + "");
    list1.addAll(list.subList(i + 1, list.size()));
    break;
    }
    }
    doCalc(list1);
    return d;
    } /**
    * 运算
    *
    * @param s1
    * @param s2
    * @param symbol
    * @return
    */ public static Double doTheMath(String s1, String s2, String symbol) {
    Double result;
    switch (symbol) {
    case ADD:
    result = Double.valueOf(s1) + Double.valueOf(s2);
    break;
    case MINUS:
    result = Double.valueOf(s1) - Double.valueOf(s2);
    break;
    case TIMES:
    result = Double.valueOf(s1) * Double.valueOf(s2);
    break;
    case DIVISION:
    result = Double.valueOf(s1) / Double.valueOf(s2);
    break;
    default:
    result = null;
    }
    return result;
    } public static void main(String[] args) {
    //String math = "9+(3-1)*3+10/2";
    String math = "12.8 + (2 - 3.55)*4+10/5.0";
    try {
    doCalc(doMatch(math));
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }
  • 结果展示

到这里关于栈的数据结构与算法就结束啦ヽ(✿゚▽゚)ノ,我认为数据结构与算法的内容还是枯燥而且有难度的,但是它的重要性不言而喻,所以大家一定要坚持下去,欢迎大家提出问题,我们一起进步!!!

数据结构与算法【Java】03---栈的更多相关文章

  1. 数据结构与算法Java描述 队列

    package com.cjm.queue; /** * 数据结构与算法Java实现 队列 * * @author 小明 * */ public class Myqueue { private Nod ...

  2. 数据结构与算法 java描述 第一章 算法及其复杂度

    目录 数据结构与算法 java描述 笔记 第一章 算法及其复杂度 算法的定义 算法性能的分析与评价 问题规模.运行时间及时间复杂度 渐进复杂度 大 O 记号 大Ω记号 Θ记号 空间复杂度 算法复杂度及 ...

  3. 数据结构与算法JavaScript (一) 栈

    序 数据结构与算法JavaScript这本书算是讲解得比较浅显的,优点就是用javascript语言把常用的数据结构给描述了下,书中很多例子来源于常见的一些面试题目,算是与时俱进,业余看了下就顺便记录 ...

  4. javascript实现数据结构与算法系列:栈 -- 顺序存储表示和链式表示及示例

    栈(Stack)是限定仅在表尾进行插入或删除操作的线性表.表尾为栈顶(top),表头为栈底(bottom),不含元素的空表为空栈. 栈又称为后进先出(last in first out)的线性表. 堆 ...

  5. PHP 程序员学数据结构与算法之《栈》

    “要成高手,必练此功”.   要成为优秀的程序员,数据结构和算法是必修的内容.而现在的Web程序员使用传统算法和数据结构都比较少,因为很多算法都是包装好的,不用我们去操心具体的实现细节,如PHP的取栈 ...

  6. JavaScript 数据结构与算法之美 - 栈内存与堆内存 、浅拷贝与深拷贝

    前言 想写好前端,先练好内功. 栈内存与堆内存 .浅拷贝与深拷贝,可以说是前端程序员的内功,要知其然,知其所以然. 笔者写的 JavaScript 数据结构与算法之美 系列用的语言是 JavaScri ...

  7. java数据结构和算法02(栈)

    什么叫做栈(Stack)呢?这里的栈和jvm的java栈可不是一个东西... 栈作为一种数据结构,我感觉栈就类似一种接口,实现的话有很多种,比如用数组.集合.链表都可以实现栈的功能,栈最大的特点就是先 ...

  8. Java数据结构与算法(3) - ch04栈(栈和转置)

    栈的基本特性是后进先出,最简单的用途是用于转置,还有其他诸如括号匹配,中序表达式(A+B*(C-D/(E+F)) --> ABCDEF+/-*+)和后续表达式(345+*612+/- --> ...

  9. 算法——Java实现栈

    栈 定义: 栈是一种先进后出的数据结构,我们把允许插入和删除的一端称为栈顶,另一端称为栈底,不含任何元素的栈称为空栈 栈的java代码实现: 基于数组: import org.junit.jupite ...

  10. 【数据结构与算法】001—栈与队列(Python)

    栈与队列 1.栈(stacks)是一种只能通过访问其一端来实现数据存储与检索的线性数据结构,具有后进先出(last in first out,LIFO)的特征 2.队列(queue)是一种具有先进先出 ...

随机推荐

  1. 定制ASP.NET 6.0的应用配置

    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进. 本文的主题是应用程序配置.要介绍的是如何使用配置.如何自定义配置,以采用不同的方式 ...

  2. Random方法中的nextInt(int arg0)方法讲解

    nextInt方法会生成一个随机的在5以内的数,负载均衡随机策略底层用的就是这个方法: Random rand = new Random(); int index = rand.nextInt(5); ...

  3. 1.3温度转换(中国大学Mooc-Python 语言程序设计)

    温度转换 温度刻画的两种不同体系 1.摄氏度:(中国等世界大多数国家使用) 以1标准大气压下水的结冰点为0度,沸点为100度,将温度进行等分刻画  2.华氏度:(美国.英国等国家使用) 以1标准大气压 ...

  4. pip国内源配置

    Python 的一大优点就是丰富的类库,所以我们经常会用 pip 来安装各种库,所以对于Python开发用户来讲,PIP安装软件包是家常便饭.但国外的源下载速度实在太慢,浪费时间.而且经常出现下载后安 ...

  5. SpringCloud 简介

    目录 什么是微服务? 初识 SpringCloud SpringCloud VS Dubbo 什么是微服务? <互联网系统架构演变> "微服务"一词源于 Martin ...

  6. 内网 Ubuntu 20.04 搭建 docusaurus 项目(或前端项目)的环境(mobaxterm、tigervnc、nfs、node)

    内网 Ubuntu 20.04 搭建 docusaurus 项目(或前端项目)的环境 背景 内网开发机是 win7,只能安装 node 14 以下,而 spug 的文档项目采用的是 Facebook ...

  7. python基础知识-day9(数据驱动)

    1.数据驱动的概念 在自动化测试中,需要把测试的数据分离到JSON,YAML等文件中. 2.YAML 的相关知识 YAML 入门教程 分类 编程技术 YAML 是 "YAML Ain't a ...

  8. 《The Tail At Scale》论文详解

    简介 用户体验与软件的流畅程度是呈正相关的,所以对于软件服务提供方来说,保持服务耗时在用户能接受的范围内就是一件必要的事情.但是在大型分布式系统上保持一个稳定的耗时又是一个很大的挑战,这篇文章解析的是 ...

  9. Nginx防御CC攻击

    CC攻击可以归为DDoS攻击的一种.他们之间都原理都是一样的,即发送大量的请求数据来导致服务器拒绝服务,是一种连接攻击.CC攻击又可分为代理CC攻击,和肉鸡CC攻击.代理CC攻击是黑客借助代理服务器生 ...

  10. 说什么也要脱单——Python WEB开发:用Tornado框架制作简易【表白墙】网站

    先来哔哔两句:(https://jq.qq.com/?_wv=1027&k=QgGWqAVF) 今天我们要用Python做Web开发,做一个简单的[表白墙]网站.众所周知表白墙的功能普遍更多的 ...