Java-----栈

目录

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 虚拟机中基于栈这种数据结构实现的用于方法调用管理的区域,而栈帧是虚拟机栈中的具体存储单元,用于承载每个方法的相关运行信息。

  • 17
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值