数据结构与算法(3)栈

article/2025/8/16 9:05:49

1、栈的一个实际需求

请输入一个表达式:7x2x2-5+1-5+3-3

在这里插入图片描述

2、栈的介绍

  • 栈的英文为stack

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

  • 栈(stack) 是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。

  • 允许插入和删除的一端,为变化的一端,称为栈顶(Top) ,另一端为固定的一端,称为栈底(Bottom)

  • 根据栈的定义可知 ,最先放入栈中元素在栈底 ,最后放入的元素在栈顶

  • 删除元素刚好相反 ,最后放入的元素最先删除,最先放入的元素最后删除

  • 图解方式说明出栈(pop) 和入栈(push) 的概念

    在这里插入图片描述

    在这里插入图片描述

3、栈的应用场景

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

4、栈的快速入门

  • 用数组模拟栈的使用,由于栈是一种有序列表,当然可以使用数组的结构来储存栈的数据内容,
    下面我们就用数组模拟栈的出栈,入栈等操作

  • 实现思路分析,并画出示意图

    在这里插入图片描述

  • 代码实现

    import java.util.Scanner;public class ArrayStackDemo {public static void main(String[] args) {//测试一下 ArrayStack 是否正确//先创建一个 ArrayStack 对象->表示栈ArrayStack stack = newArrayStack(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) {// TODO: handle exceptionSystem.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//构造器publicArrayStack(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]);}}
    }    
  • 关于栈的一个小练习:使用链表来模拟【栈】

5、栈实现综合计算器(中缀表达式)

  • 使用栈来实现综合计算器

  • 思路分析(图解)

    在这里插入图片描述

  • 代码实现【1. 先实现一位数的运算, 2. 扩展到多位数的运算】

    public class Calculator {public static void main(String[] args) {//根据前面老师思路,完成表达式的运算String expression = "7*2*2-5+1-5+3-4"; // 15//如何处理多位数的问题?//创建两个栈,数栈,一个符号栈ArrayStack2 numStack = newArrayStack2(10);ArrayStack2 operStack = newArrayStack2(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); // 1 + 3}} else { //如果是数,则直接入数栈//numStack.push(ch - 48); //? "1+3" '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//构造器publicArrayStack2(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;}
    }
    

6、逆波兰计算器

​ 完成一个逆波兰计算器,要求完成如下任务:

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

  • 支持小括号和多位数整数,这里对计算器进行简化,只支持对整数的计算

  • 思路分析

    例如: (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,由此得出最终结果
    
  • 代码实现

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Stack;
    public class PolandNotation {public static void main(String[] args) {//先定义给逆波兰表达式//(30+4)×5-6 => 30 4 + 5 × 6 - => 164// 4 * 5 - 8 + 60 + 8 / 2 => 4 5 * 8 - 60 + 8 2 / +//测试//说明为了方便,逆波兰表达式 的数字和符号使用空格隔开//String suffixExpression = "30 4 + 5 * 6 -";String suffixExpression = "4 5 * 8 - 60 + 8 2 / +"; // 76//思路//1. 先将 "3 4 + 5 × 6 - " => 放到 ArrayList 中//2. 将 ArrayList 传递给一个方法,遍历 ArrayList 配合栈 完成计算List<String> list = getListString(suffixExpression);System.out.println("rpnList=" + list);int res = calculate(list);System.out.println("计算的结果是=" + res);}//将一个逆波兰表达式, 依次将数据和运算符 放入到 ArrayList 中public static List<String> getListString(String suffixExpression) {//将 suffixExpression 分割String[] split = suffixExpression.split(" ");List<String> list = newArrayList<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 出两个数,并运算, 再入栈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());}
    }
    

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

后缀表达式适合计算式进行运算,但是人却不太容易写出来,尤其是表达式很长的情况下,因此在开发中,我们需要将中缀表达式转成后缀表达式。

(1)具体实现步骤

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

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

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

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

    • 如果 s1 为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
    • 否则,若优先级比栈顶运算符的高,也将运算符压入 s1;
    • 否则,将 s1 栈顶的运算符弹出并压入到 s2 中,再次转到(4-1)与 s1 中新的栈顶运算符相比较;
  • 遇到括号时:

    • 如果是左括号“(”,则直接压入 s1
    • 如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
  • 重复步骤 2 至 5,直到表达式的最右边

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

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

(2)举例说明

​ 将中缀表达式“1+((2+3)×4)-5”转换为后缀表达式的过程如下:

​ 因此结果为 :“1 2 3 + 4 × + 5 –”

在这里插入图片描述

(3)代码实现中缀表达式转为后缀表达式

  • 思路分析

    在这里插入图片描述

  • 代码实现

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Stack;
    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> infixExpressionList = toInfixExpressionList(expression);System.out.println("中缀表达式对应的 List=" + infixExpressionList); //ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]List<String> suffixExpreesionList = parseSuffixExpreesionList(infixExpressionList);System.out.println("后缀表达式对应的 List" + suffixExpreesionList); //ArrayList [1,2,3,+,4,*,+,5,–]System.out.printf("expression=%d", calculate(suffixExpreesionList)); // ?/*//先定义给逆波兰表达式//(30+4)×5-6 => 30 4 + 5 × 6 - => 164// 4 * 5 - 8 + 60 + 8 / 2 => 4 5 * 8 - 60 + 8 2 / +//测试//说明为了方便,逆波兰表达式 的数字和符号使用空格隔开//String suffixExpression = "30 4 + 5 * 6 -";String suffixExpression = "4 5 * 8 - 60 + 8 2 / +"; // 76//思路//1. 先将 "3 4 + 5 × 6 - " => 放到 ArrayList 中//2. 将 ArrayList 传递给一个方法,遍历 ArrayList 配合栈 完成计算List<String> list = getListString(suffixExpression);System.out.println("rpnList=" + list);int res = calculate(list);System.out.println("计算的结果是=" + res);*/}//即 ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] =》 ArrayList [1,2,3,+,4,*,+,5,–]//方法:将得到的中缀表达式对应的 List => 后缀表达式对应的 Listpublic static List<String> parseSuffixExpreesionList(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 = newArrayList<String>(); // 储存中间结果的 Lists2//遍历 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}//方法:将 中缀表达式转成对应的 List// s="1+((2+3)×4)-5";public static List<String> toInfixExpressionList(String s) {//定义一个 List,存放中缀表达式 对应的内容List<String> ls = newArrayList<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;//返回}//将一个逆波兰表达式, 依次将数据和运算符 放入到 ArrayList 中public static List<String> getListString(String suffixExpression) {//将 suffixExpression 分割String[] split = suffixExpression.split(" ");List<String> list = newArrayList<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 出两个数,并运算,再入栈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 intADD = 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;}
    }
    

8、逆波兰计算器完整版

(1)完整版的逆波兰计算器功能

  • 支持 + - * / ( )

  • 多位数,支持小数

  • 兼容处理, 过滤任何空白字符,包括空格、制表符、换页符

【说明】逆波兰计算器完整版考虑的因素较多,仅供学习。其基本思路和前面一样,也是使用到:中缀表达式转后缀表达式

(2)代码实现

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 StringADD = "+";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* @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(newArrayList<>(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 = newArrayList<>();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){caseADD : 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();}}
}

http://chatgpt.dhexx.cn/article/nPpscm06.shtml

相关文章

【数据结构】栈

栈的概念及结构 在学习栈前&#xff0c;我们先看一下栈的正式解释&#xff1a; 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。 栈中的数据元素遵守后进先出 L…

什么是栈?

本文将介绍一个重要的数据结构—栈&#xff0c;和之前讲到的链表、数组一样也是一种数据呈线性排列的数据结构&#xff0c;不过在这种结构中&#xff0c;我们只能访问最新添加的数据。栈就像是一摞书&#xff0c;拿到新书时我们会把它放在书堆的最上面&#xff0c;取书时也只能…

【图解数据结构】栈全面总结

目录 一、前言 二、基本概念 三、栈的表示和实现 1.顺序栈 2.链栈 四、栈的常见算法实现 1.初始化 2.判空 3.判满 4.顺序栈取栈顶元素 5.顺序栈入栈 6.顺序栈出栈 五、双栈 1.双端顺序栈进栈操作 2.双端顺序栈出栈操作 六、栈的应用举例 1.回文游戏 2.多进制…

数据结构——栈

栈和队列 栈和队列介绍特点栈、队列与一般线性表的区别栈——stack栈的基本运算栈的存储结构顺序栈链栈 栈的应用 栈和队列 介绍 栈和队列是在软件设计中常用的两种数据结构&#xff0c;它们的逻辑结构和线性表相同 特点 栈和队列在运算上受到了限制&#xff1a;栈是按照“…

遥感影像百分比线性拉伸

AI Earth地球科学云平台开发者模式提供了丰富的遥感数据和函数计算能力&#xff0c;下面介绍结合AIE Notebook&#xff0c;实现遥感数据的百分比线性灰度拉伸。 本期开发者实践案例——遥感影像百分比线性拉伸 灰度拉伸 (GrayScale Stretch) 是遥感影像处理过程中的重要步骤&…

遥感影像分类方法

最初的遥感影像分类是通过目视解译(濮静娟, 1984)来完成的&#xff0c;对研究人员的主观意识有较强的依赖性&#xff0c;而且效率较低&#xff0c;适用于数据量较小的情况&#xff0c;通常作为其他方法对比的对象。目前的遥感图像分类主要以计算机分类为主&#xff0c;因此按照…

遥感影像配准

文章目录 前言步骤1.ENVI&#xff1a;打开Image Registration Workflow2.Image Registration Workflow(1)选择GF2为Base Image File&#xff0c;某季节Sentinel2为Warp&#xff0c;然后Next(2)修改该参数为100(3)人为选择5个左右控制点&#xff0c;然后Next(4)删除离谱点(5)在E…

遥感图像分类技术

什么是遥感图像分类技术&#xff1f; 图像分类是将土地覆盖类别分配给像素的过程。例如&#xff0c;这9个全球土地覆盖数据集将图像分为森林、城市、农业和其他类别。 https://gisgeography.com/free-global-land-cover-land-use-data/ 一般来说&#xff0c;这是遥感中的三种主…

遥感图像分类

遥感图像分类 一、背景简介 遥感图像分类就是利用计算机通过对遥感图像中各类地物的光谱信息和空间信息进行分析&#xff0c;选择特征&#xff0c;将图像中各个像元按照某种规则或算法划分不同的类别&#xff0c;然后获得遥感图像中与实际地物的对应信息&#xff0c;从而实现…

WorldView卫星遥感影像数据/米级分辨率遥感影像

数据样例&#xff1a;百度云下载链接&#xff1a;https://pan.baidu.com/s/17ofPwpDM3OCHnE-LuhvUp 提取码&#xff1a;i0m4 目前世界上最常用的高分辨率卫星影像莫过于WORLDVIEW系列了&#xff0c;在卫星遥感圈内可谓大名鼎鼎&#xff0c;不仅具有超高的分辨率还具有其他高分…

遥感数据下载平台汇总

1中国资源卫星应用中心http://www.cresda.com.cn中巴卫星、HJ星、ZY系列 、GF系列2中科院对地观测与数字地球科学中心http://ids.ceode.ac.cn/Index.aspxERS卫星&#xff0c;Enviset_1卫星&#xff0c;法国的spot4卫星&#xff0c;中巴资源卫星&#xff0c;landset-5-73地球系统…

遥感多光谱数据下载与预处理(一、数据选择 下载)

首先说明本人并非专业大牛&#xff0c;不是教程贴只是记录一下学习过程和大家交流&#xff0c;过程有不严谨不合规范不对的地方欢迎各位大神指正。 本人目前做过接触过最多的是多光谱遥感数据&#xff0c;也是与无人机、雷达、高光谱等相比最简单的一种&#xff0c;这是我自己总…

地理空间数据云下载遥感影像

目录 1、先上网址&#xff1a;www.gscloud.cn 2、介绍界面&#xff1a; 2.1 “数据资源” 2.2 “高级检索” 1、先上网址&#xff1a;www.gscloud.cn 2、介绍界面&#xff1a; 地理空间数据云&#xff0c;作为国内免费下载遥感卫星影像的一个大平台&#xff0c;随着年代发…

遥感图像入门

遥感图像入门 一、 遥感基本概念地物光谱特性3S 技术瑞利散射大气窗口 二、 遥感系统的组成三、 遥感分类四、 遥感数字图像处理图像与数字图像数字图像获取时的基本参数数字图像类型 一、 遥感基本概念 遥感(Remote Sensing)——遥远的感知&#xff0c;在未接触物体的情况下获…

遥感影像的几何校正

一、引言&#xff08;INTRODUCTION&#xff09; 图像校正主要是指辐射校正和几何校正。辐射校正包括传感器的辐射校正、大气校正、照度校正遗迹条纹和斑点的判定和消除。几何校正就是校正成像过程中造成的各种几何畸变&#xff0c;包括几何粗校正和几何精校正。几何粗校正是针对…

遥感影像数据下载网站整理

遥感影像数据下载网站整理 1 遥感影像数据1.1 综合遥感数据1.1.1 USGS EarthExplore1.1.2 LAADS DAAC1.1.3 Copernicus Open Access Hub1.1.4 GloVis1.1.5 地理空间数据云 1.2 雷达遥感数据1.2.1 ASF DAAC 1.3 夜光遥感数据1.3.1 NOAA EOG1.3.2 珞珈一号 1.4 海洋卫星数据1.4.1…

高分GF与环境HJ系列国产卫星遥感影像数据图像免费批量下载方法

本文介绍高分&#xff08;GF&#xff09;与环境&#xff08;HJ&#xff09;等主要国产卫星遥感数据的免费下载&#xff08;包括批量下载&#xff09;方法。 首先&#xff0c;进入中国资源卫星应用中心官网&#xff1a;http://www.cresda.com/CN/。选择“查询系统”。 随后登录系…

【随笔】那些免费友好的遥感影像数据下载网站

1 .影像数据 1.1 地理空间数据云 推荐指数&#xff1a;❤❤❤❤❤交互界面&#xff1a;友好传输速度&#xff1a;0.4m/s数据集&#xff1a;开源数据集较为丰富&#xff0c;Landsat系列数据及DEM数据较丰富&#xff0c;但也有一些数据无法下载。 网址&#xff1a;地理空间数据…

完全免费的在线遥感影像下载器-转载

链接&#xff1a;link 转载学习使用&#xff0c;可免费下载&#xff01;

常用遥感数据下载平台

国内常用卫星数据下载网站 1&#xff0c;AI Earth 地球科学云平台 网址&#xff1a;AI Earth 数据&#xff1a;Landsat、Sentinel、MODIS、地形数据。 2&#xff0c;地理空间数据云 网址&#xff1a;http://www.gscloud.cn/ 数据&#xff1a;多种卫星数据 3&#xff0c;中…