当前位置: 首页 > news >正文

数据结构和算法-栈

数据结构和算法-栈

1. 栈的介绍

栈的介绍:

  1. 栈的英文为(stack)
  2. 栈是一个先入后出的有序列表
  3. 栈是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶,另一端为固定的一端,称为栈底
  4. 根据栈的定义可知,最先放入栈中元素的栈底,最后放入的元素在栈顶,而删除元素刚好相反,最后放入的元素最先删除,最先放入的元素最后删除。
  5. 出栈(POP)和入栈(PUSH)的概念

==注意:==栈和上次介绍的队列是不同的

  1. 栈的栈底是固定的,先入后出。一个指针
  2. 而队列的首尾都是不固定的,先入先出。两个指针

在这里插入图片描述

图1 出栈和入栈示意图

2. 栈的应用场景

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

3. 栈的快速入门

3.1 用数组模拟栈

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

  实现栈的思路分析

  1. 使用栈的思路分析
  2. 定义一个top来表示栈顶,初始化为-1
  3. 入栈的操作,当有数据加入到栈时,top++;stack[top]=data;
  4. 出栈的操作,int value = stack[top];top–;return value;

韩老师代码如下

//定义一个ArrayStack 表示栈
class ArrayStack {private int maxSize;    //栈的大小private int[] stack;    //数组 数组模拟栈 数据就放在该数组private int top = -1;   //top表示栈顶 初始化为-1 arr[top] 是栈的最后一个有效数据public ArrayStack(int maxSize) {this.maxSize = maxSize;stack = new int[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;}stack[++top] = value;}//出栈-pop 将栈顶的数据返回public int pop() {//先判断栈是否空if (isEmpty()) {//抛出异常处理throw new RuntimeException("栈为空");}return stack[top--];}//显示栈的情况 遍历时 需要从栈顶开始显示public void list() {//先判断栈是否空if (isEmpty()) {//抛出异常处理throw new RuntimeException("栈为空");}for (int i = top; i >= 0; i--) {System.out.println(stack[i]);}}
}

3.2 课堂作业-用链表模拟栈

自己写的代码如下

//无头结点的单链表模拟栈
class ListStack {private final int maxSize;    //栈的最大容量private Node list = new Node(0);    //栈的第一个存储空间private int top = -1;   //指向栈顶public ListStack(int maxSize) {//maxSize 最大容量this.maxSize = maxSize;Node temp = list;for (int i = 1; i < maxSize; i++) {temp.setNext(new Node(i));  //将上一个节点连接新节点temp = temp.getNext();  //将temp指向新节点(链表的最后一个节点)}}//判断栈是否满了public boolean isFull() {return top == maxSize - 1;}//判断栈是否为空public boolean isEmpty() {return top == -1;}//入栈public void push(int value) {if (isFull()) {   //如果满了System.out.println("满了兄弟");return;//退出函数}Node temp = list;++top;while (temp.getNo() != top) {//当找到时退出循环temp = temp.getNext();}//当退出循环时 temp就是目标temp.setValue(value);}//出栈-pop 将栈顶的数据返回public int pop(){if (isEmpty()){ //说明为空throw new RuntimeException("空了");}Node temp = list;while (temp.getNo() != top) {//当找到时退出循环temp = temp.getNext();}top--;return temp.getValue();}//显示栈的情况 遍历时 需要从栈顶开始显示public void show(){if (isEmpty()){ //说明为空System.out.println("空的");}for (int i = 0; i < top + 1; i++) {Node temp = list;for (int j = 0; j < top - i; j++) {temp = temp.getNext();}System.out.println(temp.getValue());}}
}//单链表的节点
class Node {private int no; //编号private int value;  //保存的值private Node next;  //next域public Node(int no) {this.no = no;}public int getNo() {return no;}public void setNo(int no) {this.no = no;}public int getValue() {return value;}public void setValue(int value) {this.value = value;}public Node getNext() {return next;}public void setNext(Node next) {this.next = next;}@Overridepublic String toString() {return "Node{" +"no=" + no +", value=" + value +", next=" + ((next == null) ? "null" : next.hashCode()) +'}';}
}

看到弹幕说有直接头插法,试了一下,发现确实更好

//有头结点的单链表模拟栈
class ListStack2 {private final int maxSize;    //栈的最大容量private Node2 head = new Node2(); //头节点private int top = -1;   //指向栈顶public ListStack2(int maxSize) {this.maxSize = maxSize;}//判断栈是否满了public boolean isFull() {return top == maxSize - 1;}//判断栈是否为空public boolean isEmpty() {return top == -1;}//入栈public void push(int value) {if (isFull()) {   //如果满了System.out.println("满了兄弟");return;//退出函数}Node2 temp = new Node2(value);//创建新节点//将此节点插入到头节点和旧的第一个节点中 成为新的第一个节点(栈顶)temp.setNext(head.getNext());head.setNext(temp);top++;//计数加一}//出栈-pop 将栈顶的数据返回public int pop() {if (isEmpty()) { //说明为空throw new RuntimeException("空了");}top--;  //计数减一int value = head.getNext().getValue();  //得到返回值//将第一个节点从链表中删除head.setNext(head.getNext().getNext()); //将头节点的next指向第二个节点 作为新的第一个节点return value;}//显示栈的情况 遍历时 需要从栈顶开始显示public void show() {if (isEmpty()) { //说明为空System.out.println("空的");return;}Node2 temp = head.getNext();while (temp != null) {//遍历完所有节点System.out.println(temp.getValue());temp = temp.getNext();//节点后移}}
}//单链表的节点
class Node2 {private int value;  //保存的值private Node2 next;  //next域public Node2(int value) {this.value = value;}public Node2() {}public int getValue() {return value;}public void setValue(int value) {this.value = value;}public Node2 getNext() {return next;}public void setNext(Node2 next) {this.next = next;}
}

4. 栈实现综合计算器

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图2 思路图

使用栈完成表达式的计算思路

  1. 通过一个index值(索引),来遍历我们的表达式
  2. 如果我们发现是一个数字,就直接入数栈
  3. 如果发现扫描到是一个符号,就分如下情况
    • 如果发现当前的符号栈为空,就直接入栈
    • 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符,就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,将得到结果,入数栈,然后将当前的操作符入符号栈,如果当前的操作符的优先级大于栈中的操作符,就直接入符号栈.
  4. 当表达式扫描完毕,就顺序的从数栈和符号栈中pop出相的数和符号,并运行.
  5. 最后在数栈只有一个数字,就是表达式的结果

自己写的多位数代码如下

package com.atguigu.stack;/*** @author 小小低头哥* @version 1.0*/public class Calculator {public static void main(String[] args) {//根据前面思路 完成表达式的运算
//        String expression = "2+2*3-2*1-1+2-3-2-3";
//        String expression = "2+3+1-4-3-2+2*20*450/50+6+4-3-4-2*2";String expression = "30*2-6*9+1";//创建两个栈 数栈、符号栈ArrayStack2 numStack = new ArrayStack2(30);ArrayStack2 operStack = new ArrayStack2(50);//定义需要的相关变量int index = 0;  //用于扫描int num1 = 0;int num2 = 0;int oper = 0;int res = 0;char ch = ' ';  //将每次扫描得到char保存到chint count = 0;  //计数器 记录是几位数 放在符号栈的符号中//开始while循环的扫描expressionwhile (true) {//依次得到expression的每一个字符ch = expression.substring(index, index + 1).charAt(0);//判断ch是什么 然后做出相应的处理if (operStack.isOper(ch)) {   //如果是运算符//判断当前的符号栈是否为空if (!operStack.isEmpty()) { //不为空则一个个判断operStack.push(count);  //先放进去//if (operStack.priority(ch) < operStack.priority(operStack.peek()))while (operStack.priority(ch) < operStack.priority(operStack.peek())) {//下面num1、oper、num2弹出的顺序不能变num1 = numStack.popN(operStack.pop());  //得到符号位前面的整数 并弹出符号栈中对应的数字标志oper = operStack.pop(); //出栈num2 = numStack.popN(operStack.pop());res = numStack.cal(num1, num2, oper);//把运算的结果放入数栈numStack.push(res);count = 1;if (!operStack.isEmpty()) {   //说明前面没有运算符了 自然也不需要更新了operStack.pop();   //把前一个运算符后面的数字位数去掉 因为此时已经变成res了 由于res是一个整体 所以相当于count=1operStack.push(count);   //更新前一个运算符后面的数字位数}else {break;}}}//为空或者判断完毕后 把当前符号入符号栈operStack.push(count);  //把符号前面的数是几位数记录下来 并放在ch的前面operStack.push(ch);count = 0;  //重新置零} else {//如果是数 则直接入数栈count++;    //数字数加一numStack.push(ch - 48); //转换为数字}//让index + 1 ,并判断是否扫描到expression最后index++;if (index >= expression.length()) {   //扫描结束operStack.push(count);  //扫描结束最后一个肯定是数字 需要把此数字的位数压入到符号栈break;}}while (true) {//下面num1、oper、num2弹出的顺序不能变num1 = numStack.popN(operStack.pop());  //得到符号位前面的整数 并弹出符号栈中对应的数字标志oper = operStack.pop(); //出栈num2 = numStack.popN(operStack.pop());if (!operStack.isEmpty() && oper == '-' && operStack.peek() == '-') {   //如果此时的操作符和上一个操作符都是负号//那么说明此时不是相减 而是相加res = numStack.cal(num1, num2, '+');} else if (!operStack.isEmpty() && oper == '+' && operStack.peek() == '-') {   //则应是num2-num1res = numStack.cal(num1, num2, '-');} else {res = numStack.cal(num1, num2, oper);}numStack.push(res); //入栈//如果符号栈为空 则计算到最后的结果,数栈中只有一个数字if (operStack.isEmpty()) {break;}operStack.pop();   //把前一个运算符后面的数字位数去掉 因为此时已经变成res了operStack.push(1);   //更新前一个运算符后面的数字位数}System.out.println(expression + "表达式的结果是:" + numStack.pop());}
}//先定义一个栈 直接使用前面创建好
//定义一个ArrayStack表示栈class ArrayStack2 {private int maxSize;    //栈的大小private int[] stack;    //数组 数组模拟栈 数据就放在该数组private int top = -1;   //top表示栈顶 初始化为-1 arr[top] 是栈的最后一个有效数据public ArrayStack2(int maxSize) {this.maxSize = maxSize;stack = new int[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;}stack[++top] = value;}//出栈 连续出栈几个并组成数字 为数栈准备public int popN(int n) {int res = 0;for (int i = 0; i < n; i++) {if (i == 0) {res = res + pop();} else {res = res + pop() * (int) Math.pow(10, i); //从个位、十位等依次入手}}return res;}//出栈-pop 将栈顶的数据返回public int pop() {//先判断栈是否空if (isEmpty()) {//抛出异常处理throw new RuntimeException("栈为空");}return stack[top--];}//显示栈的情况 遍历时 需要从栈顶开始显示public void list() {//先判断栈是否空if (isEmpty()) {//抛出异常处理throw new RuntimeException("栈为空");}for (int i = top; i >= 0; i--) {System.out.println(stack[i]);}}//返回运算符的优先级 优先级是程序员确定的 优先级使用数组表示//数字越大 则优先级越高public int priority(int oper) {if (oper == '*' || oper == '/') {return 1;} else if (oper == '+' || oper == '-') {return 0;} else {return -1;  //假定目前表达式只有加减乘除}}//增加一个方法 可以返回当前栈顶的值 但是不是真正的poppublic int peek() {return stack[top - 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;}}

韩老师写的多位数代码如下

package com.atguigu.stack;/*** @author 小小低头哥* @version 1.0*/
public class Calculator2 {public static void main(String[] args) {//根据前面思路 完成表达式的运算String expression = "30*2-6*9+1";//创建两个栈 数栈、符号栈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()) { //不为空则一个个判断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 {//如果是数 则直接入数栈//分析思路//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 = "";   //清空!!}}}//让index + 1 ,并判断是否扫描到expression最后index++;if (index >= expression.length()) {   //扫描结束break;}}while (true) {//如果符号栈为空 则计算到最后的结果,数栈中只有一个数字if (operStack.isEmpty()) {break;}num1 = numStack.pop();num2 = numStack.pop();oper = operStack.pop(); //出栈res = numStack.cal(num1, num2, oper);numStack.push(res); //入栈}System.out.println(expression + "表达式的结果是:" + numStack.pop());}
}//先定义一个栈 直接使用前面创建好
//定义一个ArrayStack表示栈class ArrayStack2 {private int maxSize;    //栈的大小private int[] stack;    //数组 数组模拟栈 数据就放在该数组private int top = -1;   //top表示栈顶 初始化为-1 arr[top] 是栈的最后一个有效数据public ArrayStack2(int maxSize) {this.maxSize = maxSize;stack = new int[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;}stack[++top] = value;}//出栈-pop 将栈顶的数据返回public int pop() {//先判断栈是否空if (isEmpty()) {//抛出异常处理throw new RuntimeException("栈为空");}return stack[top--];}//显示栈的情况 遍历时 需要从栈顶开始显示public void list() {//先判断栈是否空if (isEmpty()) {//抛出异常处理throw new RuntimeException("栈为空");}for (int i = top; i >= 0; i--) {System.out.println(stack[i]);}}//返回运算符的优先级 优先级是程序员确定的 优先级使用数组表示//数字越大 则优先级越高public int priority(int oper) {if (oper == '*' || oper == '/') {return 1;} else if (oper == '+' || oper == '-') {return 0;} else {return -1;  //假定目前表达式只有加减乘除}}//增加一个方法 可以返回当前栈顶的值 但是不是真正的poppublic int peek() {return stack[top];}//判断是不是一个运算符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;}}

4.1 课堂作业-加入小括号

在前面的基础上加上了判断小括号的功能,觉得还行 不过没加入判断负数的功能

package com.atguigu.stack;/*** @author 小小低头哥* @version 1.0*/public class Calculator {public static void main(String[] args) {//根据前面思路 完成表达式的运算
//        String expression = "2+2*3-2*1-1+2-3-2-3";
//        String expression = "2+3+1-4-3-2+2*20*450/50+6+4-3-4-2*2";String expression = "30*2-(6*9)-(1+5*4)+(4*6)";//创建两个栈 数栈、符号栈ArrayStack2 numStack = new ArrayStack2(30);ArrayStack2 operStack = new ArrayStack2(50);//定义需要的相关变量int index = 0;  //用于扫描int num1 = 0;int num2 = 0;int oper = 0;int res = 0;char ch = ' ';  //将每次扫描得到char保存到chint count = 0;  //计数器 记录是几位数 放在符号栈的符号中boolean flag = false;   //flag为真时 说明接收到了左、右括号 且还没有接收到符号位//开始while循环的扫描expressionwhile (true) {//依次得到expression的每一个字符ch = expression.substring(index, index + 1).charAt(0);//判断ch是什么 然后做出相应的处理if (operStack.isOper(ch)) {   //如果是运算符//判断当前的符号栈是否为空if (!operStack.isEmpty()) { //不为空则一个个判断if (!flag) {//如果上一个是 ( 则不用放进去operStack.push(count);  //先放进去}flag = false;//if (operStack.priority(ch) < operStack.priority(operStack.peek()))while (operStack.priority(ch) < operStack.priority(operStack.peek(1))) {//0的优先权最小 不会进入循环 正要如此//下面num1、oper、num2弹出的顺序不能变num1 = numStack.popN(operStack.pop());  //得到符号位前面的整数 并弹出符号栈中对应的数字标志oper = operStack.pop(); //出栈num2 = numStack.popN(operStack.pop());res = numStack.cal(num1, num2, oper);//把运算的结果放入数栈numStack.push(res);count = 1;//前面还有运算符了 需要更新 或者计算完括号中的数if (!operStack.isEmpty() && operStack.peek(0) != 0) {operStack.pop();   //把前一个运算符后面的数字位数去掉 因为此时已经变成res了 由于res是一个整体 所以相当于count=1operStack.push(count);   //更新前一个运算符后面的数字位数} else {break;}}}//为空或者判断完毕后 把当前符号入符号栈operStack.push(count);  //把符号前面的数是几位数记录下来 并放在ch的前面operStack.push(ch);count = 0;  //重新置零} else if (ch == '(') {   //按数学规矩 (的前面一个肯定是运算符flag = true;operStack.push(0);  //把零送进去当作是起点} else if (ch == ')') {flag = true;operStack.push(count);  //)前必然是数字 所以这里先送进去一个计数器while (true) {//说明还没计算完括号内的运算//下面num1、oper、num2弹出的顺序不能变num1 = numStack.popN(operStack.pop());  //得到符号位前面的整数 并弹出符号栈中对应的数字标志oper = operStack.pop(); //出栈num2 = numStack.popN(operStack.pop());if (operStack.peek(0)!= 0 && oper == '-' && operStack.peek(1) == '-') {   //如果此时的操作符和上一个操作符都是负号//那么说明此时不是相减 而是相加res = numStack.cal(num1, num2, '+');} else if (operStack.peek(0)!= 0 && oper == '+' && operStack.peek(1) == '-') {   //则应是num2-num1res = numStack.cal(num1, num2, '-');} else {res = numStack.cal(num1, num2, oper);}numStack.push(res); //入栈//如果括号中的数计算完毕if (operStack.peek(0) == 0) {operStack.pop();    //把标志位给弹出来operStack.push(1);  //因为()的结果是一个整数 所以把1送进去作为()整体的数的个数break;}operStack.pop();   //把前一个运算符后面的数字位数去掉 因为此时已经变成res了operStack.push(1);   //更新前一个运算符后面的数字位数}} else {//如果是数 则直接入数栈count++;    //数字数加一numStack.push(ch - 48); //转换为数字}//让index + 1 ,并判断是否扫描到expression最后index++;if (index >= expression.length() ) {   //扫描结束//扫描结束最后一个肯定是数字 需要把此数字的位数压入到符号栈if( ch != ')'){ //但是如果最后是以)结束 则由于括号计算中已经把1插进去了 不需要了operStack.push(count);}break;}}while (true) {//下面num1、oper、num2弹出的顺序不能变num1 = numStack.popN(operStack.pop());  //得到符号位前面的整数 并弹出符号栈中对应的数字标志oper = operStack.pop(); //出栈num2 = numStack.popN(operStack.pop());if (!operStack.isEmpty() && oper == '-' && operStack.peek(1) == '-') {   //如果此时的操作符和上一个操作符都是负号//那么说明此时不是相减 而是相加res = numStack.cal(num1, num2, '+');} else if (!operStack.isEmpty() && oper == '+' && operStack.peek(1) == '-') {   //则应是num2-num1res = numStack.cal(num1, num2, '-');} else {res = numStack.cal(num1, num2, oper);}numStack.push(res); //入栈//如果符号栈为空 则计算到最后的结果,数栈中只有一个数字if (operStack.isEmpty()) {break;}operStack.pop();   //把前一个运算符后面的数字位数去掉 因为此时已经变成res了operStack.push(1);   //更新前一个运算符后面的数字位数}System.out.println(expression + "表达式的结果是:" + numStack.pop());}
}//先定义一个栈 直接使用前面创建好
//定义一个ArrayStack表示栈class ArrayStack2 {private int maxSize;    //栈的大小private int[] stack;    //数组 数组模拟栈 数据就放在该数组private int top = -1;   //top表示栈顶 初始化为-1 arr[top] 是栈的最后一个有效数据public ArrayStack2(int maxSize) {this.maxSize = maxSize;stack = new int[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;}stack[++top] = value;}//出栈 连续出栈几个并组成数字 为数栈准备public int popN(int n) {int res = 0;for (int i = 0; i < n; i++) {if (i == 0) {res = res + pop();} else {res = res + pop() * (int) Math.pow(10, i); //从个位、十位等依次入手}}return res;}//出栈-pop 将栈顶的数据返回public int pop() {//先判断栈是否空if (isEmpty()) {//抛出异常处理throw new RuntimeException("栈为空");}return stack[top--];}//显示栈的情况 遍历时 需要从栈顶开始显示public void list() {//先判断栈是否空if (isEmpty()) {//抛出异常处理throw new RuntimeException("栈为空");}for (int i = top; i >= 0; i--) {System.out.println(stack[i]);}}//返回运算符的优先级 优先级是程序员确定的 优先级使用数组表示//数字越大 则优先级越高public int priority(int oper) {if (oper == '*' || oper == '/') {return 1;} else if (oper == '+' || oper == '-') {return 0;} else {return -1;  //假定目前表达式只有加减乘除}}//增加一个方法 可以返回当前栈顶的第n个值 但是不是真正的poppublic int peek(int n) {return stack[top - n];}//判断是不是一个运算符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. 栈的介绍 栈的介绍&#xff1a; 栈的英文为(stack)栈是一个先入后出的有序列表栈是限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端&#xff0c;为变化的一端&#xff0c;称为栈顶&#xff0c;另一端为固…...

C#基础与进阶扩展合集-进阶篇(持续更新)

目录 本文分两篇&#xff0c;基础篇点击&#xff1a;C#基础与进阶扩展合集-基础篇 一、进阶 1、Predicate 2、设置C#语言版本 3、ListCollectionView过滤集合 4、值类型与引用类型 5、程序设置当前项目工作目录 6、获取App.config配置文件中的值 7、Linq常用语句 8、…...

快速入门GitHub 之超简单的注册方法和超好用的使用技巧

最近几天发现有些人对Github网站很好奇,但是无奈自己不会用,因为是外国人的网站,首先自己的英文就不过关。对于这个,其实可以用谷歌浏览器去浏览Github,它有一键翻译的功能。但还是有必要介绍一下关于Github的一些功能和具体操作,初学编程语言的小伙伴们一定对 GitHub 有…...

ESP32-Web-Server编程- 在 Web 上开发动态纪念册

ESP32-Web-Server编程- 在 Web 上开发动态纪念册 概述 Web 有很多有趣的玩法&#xff0c;在打开网页的同时送她一个惊喜。 需求及功能解析 本节演示在 ESP32 上部署一个 Web&#xff0c;当打开对应的网页时&#xff0c;将运行动态的网页内容&#xff0c;显示炫酷的纪念贺词…...

双向ESD保护 汽车级TVS二极管 ESD9B3.3ST5G工作原理、特性参数、封装形式

什么是汽车级TVS二极管&#xff1f; TVS二极管是一种用于保护电子电路的电子元件。它主要用于电路中的过电压保护&#xff0c;防止电压过高而损坏其他部件。TVS二极管通常被称为“汽车级”是因为它们能够满足汽车电子系统的特殊要求。 在汽车电子系统中&#xff0c;由于车辆启…...

Ribbon-IRule 修改负载均衡的规则

1、负载均衡规则描述 &#xff08;1&#xff09;整体关系 &#xff08;2&#xff09;规则描述 内置负载均衡规则类规则描述RoundRobinRule简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。AvailabilityFilteringRule对以下两种服务器进行忽略: (1)在默认情况下&…...

双十二电视盒子哪个牌子最好?自费3000+测评整理电视盒子推荐

双十二不知道电视盒子哪个牌子最好的新手很多&#xff0c;想要我分享电视盒子推荐&#xff0c;为结果更客观我花费三千多购入了十几款热销电视盒子&#xff0c;通过一个月时间的全面对比测评后整理了电视盒子推荐&#xff0c;给双十二不知道怎么选电视盒子的朋友们提供参考。 一…...

排序:直接选择排序

直接选择排序&#xff1a; 本质&#xff1a; 直接选择排序的本质就是在数组中进行遍历挑选出最大的元素&#xff0c;讲最大的元素放到对应的位置后&#xff0c;再次选出次大的位置&#xff0c;而后又放到对应的位置..........................直到数组成为一个有序序列。 优…...

Nacos多数据源插件

Nacos从2.2.0版本开始,可通过SPI机制注入多数据源实现插件,并在引入对应数据源实现后,便可在Nacos启动时通过读取application.properties配置文件中spring.datasource.platform配置项选择加载对应多数据源插件.本文档详细介绍一个多数据源插件如何实现以及如何使其生效。 注意:…...

【Java基础篇 | 面向对象】—— 聊聊什么是接口(上篇)

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【JavaSE_primary】 本专栏旨在分享学习JavaSE的一点学习心得&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 关于接口的简单的介绍…...

golang实现函数yamlToStruct(infile,outFile)

问&#xff1a; golang实现函数yamlToStruct(infile,outFile),将yaml文件格式化成golang的结构体 gpt: 要实现一个将YAML文件格式化成Golang结构体的函数&#xff0c;你可以使用 yaml 和 reflect 包来处理。首先&#xff0c;你需要使用 yaml.Unmarshal 函数将YAML文件解析为一…...

产品成本收集器流程演示

感谢大佬的文章&#xff0c;我只是一个翻译搬运工&#xff0c;原文地址&#xff1a;产品成本收集器 概述 SAP 令人兴奋的部分之一是它在不同操作模块之间的集成程度。使用产品成本收集器来跟踪生产就是一个很好的例子。在本博客中&#xff0c;我计划遵循产品成本收集器流程&a…...

【微服务】springboot整合quartz使用详解

目录 一、前言 二、quartz介绍 2.1 quartz概述 2.2 quartz优缺点 2.3 quartz核心概念 2.3.1 Scheduler 2.3.2 Trigger 2.3.3 Job 2.3.4 JobDetail 2.4 Quartz作业存储类型 2.5 适用场景 三、Cron表达式 3.1 Cron表达式语法 3.2 Cron表达式各元素说明 3.3 Cron表达…...

Electron+Ts+Vue+Vite桌面应用系列:TypeScript常用时间处理工具

文章目录 1️⃣ 时间处理工具1.1 格式化时间1.2 把时间戳改成日期格式1.3 Day.js 工具类使用1.4 date-fns 工具类使用 优质资源分享 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/134712978 ElectronTsVueVite桌面应用…...

记录 | centos源码编译bazel

tensorflow的源码编译依赖于 bazel 这里进行 bazel 的源码编译 1、安装依赖 sudo yum install -y java-11-openjdk sudo yum install -y java-11-openjdk-devel sudo yum install -y protobuf-compiler zip unzip2、知悉要安装的 bazel 的版本 务必安装受支持的 Bazel 版本…...

常见的Bean工厂后置处理器

此代码在jdk11上测试通过&#xff0c;SpringBoot版本为2.7.14 1.上代码 导入坐标 <dependencies><!-- spring数据坐标 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-rest</art…...

代码随想录算法训练营第四十二天| 416 分割等和子集

目录 416 分割等和子集 416 分割等和子集 class Solution { public:const int N 210;bool canPartition(vector<int>& nums) {vector<int>f(N);int sum 0;for(auto num : nums)sum num;if(sum % 2 1)return false;//如果int target sum / 2;for(int i …...

memmove 和 memcpy的区别

函数原型及作用 memcpy 和 memmove 都是C语言中的库函数&#xff0c;在头文件string.h中&#xff0c;作用是拷贝一定长度的内存的内容&#xff0c;原型分别如下&#xff1a; void* memcpy(void *dst, const void *src, size_t count); void* memmove(void *dst, const void *…...

C实现的双向链表队列

如下代码所示&#xff0c;一个头文件实现的双向链表&#xff0c;用c代码实现&#xff1a; #ifndef _LINUX_LIST_H #define _LINUX_LIST_H#include "stddef.h" #include "poison.h"#ifndef ARCH_HAS_PREFETCH #define ARCH_HAS_PREFETCH static inline voi…...

自适应中值滤波器的python代码实现-----冈萨雷斯数字图像处理

基本原理&#xff1a; 自适应中值滤波器是一种图像处理技术&#xff0c;用于去除图像中的噪声。其原理是根据像素周围邻域内像素值的特性&#xff0c;动态地选择滤波器的大小和中值滤波的程度。 **邻域选择&#xff1a;**对于每个像素点&#xff0c;选取一个窗口或者邻域&…...

Python作业答疑_6.22~6.25

一、Python 一班 1. 基数分割列表 1.1 问题描述 给定一无序数列&#xff0c;把数列的第一个数字当成基数&#xff0c;让数列中基数小的数字排在数列前面&#xff0c;比基数大的数字排在数列的后面。 1.2 问题示例 如数列&#xff1a;num[4,1,8,3,9,2,10,7]。基数为 4&…...

Uber Go 语言编码规范

uber-go/guide 的中文翻译 English 文档链接 Uber Go 语言编码规范 Uber 是一家美国硅谷的科技公司&#xff0c;也是 Go 语言的早期 adopter。其开源了很多 golang 项目&#xff0c;诸如被 Gopher 圈熟知的 zap、jaeger 等。2018 年年末 Uber 将内部的 Go 风格规范 开源到 G…...

UniRepLKNet:用于音频、视频、点云、时间序列和图像识别的通用感知大内核ConvNet

摘要 https://arxiv.org/abs/2311.15599 大核卷积神经网络(ConvNets)最近受到了广泛的研究关注,但存在两个未解决的关键问题需要进一步研究。(1)现有大核ConvNets的架构在很大程度上遵循传统ConvNets或Transformers的设计原则,而大核ConvNets的架构设计仍未得到充分解决。(2…...

Http协议与Tomcat

HTTP协议 HTTP协议&#xff08;HyperText Transfer Protocol&#xff09;即超文本传输协议 &#xff0c;是TCP/IC网络体系结构应用层的一个客户端-服务端协议&#xff0c;是所有客户端&#xff0c;服务端数据传输的基石&#xff08;数据传输规则&#xff09; 特点 ⭐基于TCP协…...

Spring AOP从入门到精通

目录 1. AOP的演化过程 1. 代理模式 2. 动态代理 2.1 JDK动态代理 2.2 Cglib动态代理 3. Spring模式 3.1 ProxyFactory 3.2 ProxyFactoryBean 3.3 AbstractAutoProxyCreator 2. Spring AOP抽象 1. 核心术语 1.1 连接点(JoinPoint) 1.2 切点(Pointcut) 1.3 增强(Ad…...

Tap虚拟网卡

1 概述 Tap设备通常用于虚拟化场景下&#xff0c;其驱动代码位于drivers/net/tun.c&#xff0c;tap与tun复用大部分代码&#xff0c; 注&#xff1a;drivers/net/tap.c并不是tap设备的代码&#xff0c;而是macvtap和ipvtap&#xff1b; 下文中&#xff0c;我们统一称tap&#…...

【数电笔记】53-与非门构成的基本RS触发器

目录 说明&#xff1a; 1. 电路组成 2. 逻辑功能 3. 特性表 4. 特性方程 5. 状态转换图 6. 驱动表 7. 例题 例1 例2 说明&#xff1a; 笔记配套视频来源&#xff1a;B站&#xff1b;本系列笔记并未记录所有章节&#xff0c;只对个人认为重要章节做了笔记&#xff1b…...

kubernetes(k8s)容器内无法连接同所绑定的Service ClusterIP问题记录

kubernetes(k8s)容器内无法连接同所绑定的Service ClusterIP问题记录 1. k8s环境 k8s使用kubernetes-server-linux-amd64_1.19.10.tar.gz 二进制bin 的方式手动部署 k8s 版本: [rootmaster ~]# kubectl version Client Version: version.Info{Major:"1", Minor:&…...

Hadoop入门学习笔记

视频课程地址&#xff1a;https://www.bilibili.com/video/BV1WY4y197g7 课程资料链接&#xff1a;https://pan.baidu.com/s/15KpnWeKpvExpKmOC8xjmtQ?pwd5ay8 这里写目录标题 一、VMware准备Linux虚拟机1.1. VMware安装Linux虚拟机1.1.1. 修改虚拟机子网IP和网关1.1.2. 安装…...

堆栈,BSS,DATA,TEXT

一、目标文件 首先目标文件的构成&#xff0c;Linux下就是.o 文件 编译器编译源码后生成的文件叫目标文件&#xff08;Object File&#xff09;。 目标文件和可执行文件一般采用同一种格式&#xff0c;这种存储格式为 ELF。 目前文件的内容至少有编译后的机器指令代码和数据&a…...

Java八股文面试全套真题【含答案】-JSON篇

什么是JSON&#xff1f; 答案&#xff1a;JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;基于JavaScript的对象字面量表示法&#xff0c;用于在不同语言和平台之间传输数据。JSON的数据结构是怎样的&#xff1f; 答案&#xf…...

数据库管理-第119期 记一次迁移和性能优化(202301130)

数据库管理-第119期 记一次迁移和性能优化&#xff08;202301130&#xff09; 1 迁移 之前因为DV组件没有迁移成功的那个PDB&#xff0c;后来想着在目标端安装DV组件迁移&#xff0c;结果目标端装不上&#xff0c;而且开了SR也没看出个所以然来。只能换一个方向&#xff0c;尝…...

【云原生-K8s】镜像漏洞安全扫描工具Trivy部署及使用

基础介绍基础描述Trivy特点 部署在线下载百度网盘下载安装 使用扫描nginx镜像扫描结果解析json格式输出 总结 基础介绍 基础描述 Trivy是一个开源的容器镜像漏洞扫描器&#xff0c;可以扫描常见的操作系统和应用程序依赖项的漏洞。它可以与Docker和Kubernetes集成&#xff0c;…...

【Docker】Swarm的ingress网络

Docker Swarm Ingress网络是Docker集群中的一种网络模式&#xff0c;它允许在Swarm集群中运行的服务通过一个公共的入口点进行访问。Ingress网络将外部流量路由到Swarm集群中的适当服务&#xff0c;并提供负载均衡和服务发现功能。 在Docker Swarm中&#xff0c;Ingress网络使…...

gcc安全特性之FORTIFY_SOURCE

GCC 4.0引入了FORTIFY_SOURCE特性&#xff0c;旨在加强程序的安全性&#xff0c;特别是对于字符串和内存操作函数的使用。下面是对FORTIFY_SOURCE机制的深入分析&#xff1a; 1. 功能 FORTIFY_SOURCE旨在检测和防止缓冲区溢出&#xff0c;格式化字符串漏洞以及其他与内存操作…...

【JUC】二十、volatile变量的特点与使用场景

文章目录 1、volatile可见性案例2、线程工作内存与主内存之间的原子操作3、volatile变量不具有原子性案例4、无原子性的原因分析&#xff1a;i5、volatile变量小总结6、重排序7、volatile变量禁重排的案例8、日常使用场景9、总结 volatile变量的特点&#xff1a; 可见性禁重排无…...

软件工程期末复习(2)

学习资料 设计模式与软件体系结构【期末全整理答案】_软件设计模式与体系结构期末考试题_鸽子不二的博客-CSDN博客 软件设计与体系结构(第二版)部分习题_软件设计与体系结构第二版课后答案-CSDN博客 软件体系结构试题库试题和答案 - 豆丁网Docin 软件设计与体系结构复习 - CN…...

[vue3] 使用 vite 创建vue3项目的详细流程

一、vite介绍 Vite&#xff08;法语意为 “快速的”&#xff0c;发音 /vit/&#xff0c;发音同 “veet”) 是一种新型前端构建工具&#xff0c;能够显著提升前端开发体验&#xff08;热更新、打包构建速度更快&#xff09;。 二、使用vite构建项目 【学习指南】学习新技能最…...

#HarmonyOS:软件安装window和mac预览Hello World

Window软件地址 https://developer.harmonyos.com/cn/develop/deveco-studio#download 安装的建议 这个界面这样选&#xff0c;其他界面全部按照默认路径往下走&#xff01;&#xff01;&#xff01; 等待安装… 安装环境错误处理 一般就是本地node配置异常导致&#xff…...

nginx 一键切换停机维护页面 —— 筑梦之路

背景说明 进行停机维护或者系统升级等操作&#xff0c;会影响到用户使用&#xff0c;如果停机维护期间用户未看到停机维护的通知&#xff0c;仍去访问系统&#xff0c;会提示默认不太友好的访问错误界面 &#xff0c;这时如果在维护的时候直接展示停机公告的具体信息&#xff0…...

Python作业答疑

1. 旋转字符串 1.1 问题描述 给定一个字符串&#xff08;以字符数组的形式&#xff09;和一个偏移量&#xff0c;根据偏移量原地从左向右旋转字符串。 1.2 问题示例 输入str"abcdefg"&#xff0c;offset3&#xff0c;输出"efgabcd"。 输入str"ab…...

计算机网络实用工具之Hydra

简介 Hydra 是一个并行登录破解程序&#xff0c;支持多种协议进行攻击。它非常快速且灵活&#xff0c;并且很容易添加新模块。 该工具使研究人员和安全顾问能够展示远程未经授权访问系统是多么容易。 目前该工具支持以下协议&#xff1a; Asterisk, AFP, Cisco AAA, Cisco au…...

AUTOSAR 入门

前言 AUTOSAR是什么Vector DaVinci 工具功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个注脚注释也是必…...

新版IDEA中,module模块无法被识别,类全部变成咖啡杯无法被识

新版IDEA中&#xff0c;module模块无法被识别&#xff0c;类全部变成咖啡杯无法被识 如下图&#xff1a; 解决方法&#xff1a;java的Directory文件没有被设置为根目录&#xff0c;解决方法如下&#xff1a; 这是方法之一&#xff0c;还有很多的原因 可能的原因&#xff1a; …...

vue.js el-table 动态单元格列合并

一、业务需求&#xff1a; 一个展示列表&#xff0c;表格中有一部分列是根据后端接口动态展示&#xff0c;对于不同类型的数据展示效果不一样。如果接口返回数据是’类型1‘的&#xff0c;则正常展示&#xff0c;如果是’类型2‘的数据&#xff0c;则合并当前数据的动态表格。…...

word模板导出word文件

前期准备工作word模板 右键字段如果无编辑域 ctrlF9 一下&#xff0c;然后再右键 wps 直接 ctrlF9 会变成编辑域 pom.xml所需依赖 <dependencies> <!--word 依赖--> <dependency><groupId>fr.opensagres.xdocreport</groupId><artifactId…...

debianubuntu的nvidia驱动升级

背景 给出的机器的驱动版本不符合要求&#xff0c;使用自定义的驱动版本。 前置 如果使用nvidia官方的.run安装的驱动包&#xff0c;可以使用系统自带的nvidia-uninstall命令卸载比较方便&#xff0c;不建议使用apt pruge nvidia-*命令删除。会带来其他的问题。 卸载完成之…...

【开源视频联动物联网平台】视频接入网关的用法

视频接入网关是一种功能强大的视频网关设备&#xff0c;能够解决各种视频接入、视频输出、视频转码和视频融合等问题。它可以在应急指挥、智慧融合等项目中发挥重要作用&#xff0c;与各种系统进行对接&#xff0c;解决视频能力跨系统集成的难题。 很多视频接入网关在接入协议…...

【bug排查解决】现象级延迟8-10s

业务背景 最近公司在做物联网相关的项目&#xff0c;调试过程中发现好玩的bug。 首先一个数据采集场景&#xff0c;plc采集数据全链路&#xff1a; kepServer&#xff08;kepserver IOT gateway&#xff09; -> emqx &#xff08;查看日志&#xff09;-> iot服务 -> 业…...

【人生感悟】不能对一个人太好是有心理学原理的

1、不能对一个人太好是有心理学原理的&#xff0c;当你长期友善对待一个人时&#xff0c;如果这个人认知程度不是很高&#xff0c;层次稍微的偏低&#xff0c;那他可能直接把你的友善理解为理所应当&#xff0c;甚至是你在讨好他&#xff0c;还会把你们之间的关系理解成他是高于…...