目录
1.栈(Stack)
1.1概念
1.2栈的使用
1.3栈的模拟实现
1.4栈的应用场景
1.5栈、虚拟机栈、栈帧有什么区别呢
1.栈(Stack)
1.1概念
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈 顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据在栈顶。
1.2栈的使用
方法 | 功能 |
Stack() | 构造一个空的栈 |
E push(E e) | 将e入栈,并返回e |
E pop() | 将栈顶元素出栈并返回 |
E peek() | 获取栈顶元素 |
int size() | 获取栈中有效元素个数 |
boolean empty() | 检测栈是否为空 |
public static void main(String[] args) {
Stack<Integer>stack=new Stack<>();
//进栈
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
stack.push(5);
System.out.println(stack);
//出栈
stack.pop();
stack.pop();
System.out.println(stack);
//判断栈是否为空
System.out.println(stack.empty());
//获取栈顶元素
System.out.println(stack.peek());
//获取元素个数
System.out.println(stack.size());
}
1.3栈的模拟实现
使用数组实现
package MyStack;
import java.util.Arrays;
public class MyStack {
public int[]elem;
public int useSize=0;
//构造方法
public MyStack() {
this.elem =new int[10];
}
//出栈
public int pop(){
if(empty()){
throw new StackemptyExpection("栈为空,无法出栈");
}
return elem[useSize--];
}
//进栈
public void push(int val){
if(isFull()){
this.elem= Arrays.copyOf(elem,elem.length*2);
}
elem[useSize]=val;
useSize++;
}
//获取栈顶元素
public int peek(){
return elem[useSize-1];
}
//判断是否为空
public boolean empty(){
return useSize==0;
}
//判断是否满了
public boolean isFull(){
return useSize== elem.length;
}
}
1.4栈的应用场景
1. 改变元素的序列
1. 若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()
A: 1,4,3,2 B: 2,3,4,1 C: 3,1,4,2 D: 3,4,2,1
答案:C
2.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出栈的顺序是( )。
A: 12345ABCDE B: EDCBA54321 C: ABCDE12345 D: 54321EDCBA
答案:B
2. 将递归转化为循环
逆序打印链表
// 递归方式
void printList(Node head){
if(null != head){
printList(head.next);
System.out.print(head.val + " ");
}
}
// 循环方式
void printList(Node head){
if(null == head){
return;
}
Stack<Node> s = new Stack<>();
// 将链表中的结点保存在栈中
Node cur = head;
while(null != cur){
s.push(cur);
cur = cur.next;
}
// 将栈中的元素出栈
while(!s.empty()){
System.out.print(s.pop().val + " ");
}
}
3.有效的括号
. - 力扣(LeetCode)
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
方法:
创建一个栈用来存储左括号,遍历字符串,如果是左括号就将其进栈,否则判断其是否和栈顶括号匹配,如果栈顶没有元素返回false,如果匹配就出栈,遍历完看栈是否为空,不为空返回false,为空返回true
class Solution {
public boolean isValid(String s) {
Stack<Character>stack=new Stack<>();
for(int i=0;i<s.length();i++){
char ch=s.charAt(i);
if(ch=='('||ch=='{'||ch=='['){
stack.push(ch);
}else{
if(stack.isEmpty()){
return false;
}
char ch2=stack.peek();
if((ch==')'&&ch2=='(')||(ch=='}'&&ch2=='{')||(ch==']'&&ch2=='['))
{
stack.pop();
}else{
return false;
}
}
}
if(!stack.isEmpty()){
return false;
}
return true;
}
}
4.逆波兰表达式求值
. - 力扣(LeetCode)
给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
方法:
创建一个栈来存储数字,遍历字符串数组,如果是数字就进栈,不是就连续出栈两个数字进行计算,计算的结果再进栈,遍历完数组后,返回栈顶元素
class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer>stack=new Stack<>();
for(String str:tokens){
if(!isOperator(str)){
int x=Integer.parseInt(str);
stack.push(x);
}else{
int val2=stack.pop();
int val1=stack.pop();
switch(str){
case "+":
stack.push(val1+val2);
break;
case "-":
stack.push(val1-val2);
break;
case "*":
stack.push(val1*val2);
break;
case "/":
stack.push(val1/val2);
break;
}
}
}
return stack.pop();
}
private boolean isOperator(String str){
if(str.equals("+")||str.equals("-")||str.equals("/")||str.equals("*")){
return true;
}
return false;
}
}
中缀表达式怎么变后缀表达式
将中缀表达式转换为后缀表达式可以通过以下步骤进行:
1. 初始化一个空栈用于存储运算符,以及一个空字符串用于存储后缀表达式。
2. 从左到右依次扫描中缀表达式的每个元素。
• 如果是操作数,直接添加到后缀表达式中。
• 如果是左括号,将其入栈。
• 如果是右括号,将栈中的运算符依次弹出并添加到后缀表达式中,直到遇到左括号,然后将左括号从栈中弹出。
• 如果是运算符:
• 若栈为空或栈顶为左括号,直接将运算符入栈。
• 若该运算符的优先级高于栈顶运算符的优先级,将其入栈。
• 若该运算符的优先级低于或等于栈顶运算符的优先级,将栈顶运算符弹出并添加到后缀表达式中,然后比较新的栈顶运算符,重复此操作,直到该运算符可以入栈。
3. 扫描完毕后,将栈中剩余的运算符依次弹出并添加到后缀表达式中。
例如,将中缀表达式“3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3”转换为后缀表达式:
扫描到“3”,是操作数,直接添加到后缀表达式,后缀表达式为“3”。
扫描到“+”,栈为空,入栈,栈内为“+”,后缀表达式为“3”。
扫描到“4”,是操作数,添加到后缀表达式,后缀表达式为“3 4”。
扫描到“”,“+”优先级低于“”,“*”入栈,栈内为“+ *”,后缀表达式为“3 4”。
扫描到“2”,是操作数,添加到后缀表达式,后缀表达式为“3 4 2”。
扫描到“/”,“”优先级高于“/”,将“”弹出,“/”入栈,栈内为“+ /”,后缀表达式为“3 4 2 *”。
扫描到“(”,入栈,栈内为“+ / (”,后缀表达式为“3 4 2 *”。
扫描到“1”,是操作数,添加到后缀表达式,后缀表达式为“3 4 2 * 1”。
扫描到“-”,栈顶为“(”,入栈,栈内为“+ / ( -”,后缀表达式为“3 4 2 * 1”。
扫描到“5”,是操作数,添加到后缀表达式,后缀表达式为“3 4 2 * 1 5”。
扫描到“)”,将栈中运算符依次弹出直到遇到“(”,“(”弹出,栈内为“+ / ”,后缀表达式为“3 4 2 * 1 5 -”。
扫描到“^”,“/”优先级低于“^”,“^”入栈,栈内为“+ / ^”,后缀表达式为“3 4 2 * 1 5 -”。
扫描到“2”,是操作数,添加到后缀表达式,后缀表达式为“3 4 2 * 1 5 - 2”。
扫描到“^”,“^”优先级高于栈顶“^”,入栈,栈内为“+ / ^ ^”,后缀表达式为“3 4 2 * 1 5 - 2”。
扫描到“3”,是操作数,添加到后缀表达式,后缀表达式为“3 4 2 * 1 5 - 2 3”。
扫描结束,将栈中运算符依次弹出添加到后缀表达式,最终后缀表达式为“3 4 2 * 1 5 - 2 ^ 3 ^ / + ”
5.栈的压入和弹出序列
栈的压入、弹出序列_牛客题霸_牛客网
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。
1. 0<=pushV.length == popV.length <=1000
2. -1000<=pushV[i]<=1000
3. pushV 的所有数字均不相同
方法:
遍历一遍pushV数组,先将pushV中的元素压栈,判断栈顶元素是否和popV的j下标元素相同,相同就出栈,继续判断,直到不相同为止,当遍历完pushV时,看栈是否为空,如果为空,则说明popV是可行的出栈序列,否则不是
public boolean IsPopOrder (int[] pushV, int[] popV) {
Stack<Integer>stack=new Stack<>();
int j=0;
for(int i=0;i<pushV.length;i++){
stack.push(pushV[i]);
while(!stack.empty()&&stack.peek()==popV[j]&&j<popV.length){
stack.pop();
j++;
}
}
return stack.empty();
}
}
6.最小栈
. - 力扣(LeetCode)
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。
方法
构造方法创建一个普通栈和最小栈
push方法,普通栈是必须压栈的,当普通栈压栈完成后,比较普通栈和最小栈的栈顶元素,如果普通栈的栈顶元素小于或等于最小栈的栈顶元素,那么就对最小栈进行压栈
pop方法,接收普通栈的栈顶元素,普通栈出栈,如果栈顶元素和最小栈的栈顶元素相同,那么最小栈也出栈
top方法,如果普通栈为空,返回-1,否则返回普通栈的栈顶元素
getMin方法,如果最小栈为空,返回-1,否则返回最小栈的栈顶元素
class MinStack {
public Stack<Integer>stack;
public Stack<Integer>Minstack;
public MinStack() {
this.stack=new Stack<>();
this.Minstack=new Stack<>();
}
public void push(int val) {
stack.push(val);
if(Minstack.empty()){
Minstack.push(val);
}else{
if(val<=Minstack.peek()){
Minstack.push(val);
}
}
}
public void pop() {
int popVal=stack.pop();
if(popVal==Minstack.peek()){
Minstack.pop();
}
}
public int top() {
if(stack.empty()){
return -1;
}
return stack.peek();
}
public int getMin() {
if(Minstack.empty()){
return -1;
}
return Minstack.peek();
}
}
1.5栈、虚拟机栈、栈帧有什么区别呢?
栈(Stack) | 是一种数据结构,具有“后进先出”的特点,常用于存储临时数据和函数调用信息。 |
虚拟机栈(Virtual Machine Stack) | 是 Java 虚拟机运行时数据区中的一部分,用于存储方法调用的相关信息,包括局部变量表、操作数栈、动态链接、方法出口等。 |
栈帧(Stack Frame) | 是虚拟机栈中的基本单位,每当一个方法被调用时,就会创建一个新的栈帧并压入虚拟机栈。栈帧包含了方法的局部变量、操作数栈、返回地址等信息。当方法执行完毕,对应的栈帧就会出栈。 |
总的来说,栈是一种通用的数据结构概念,虚拟机栈是 Java 虚拟机中基于栈这种数据结构实现的用于方法调用管理的区域,而栈帧是虚拟机栈中的具体存储单元,用于承载每个方法的相关运行信息。