【数据结构】_5.栈

目录

1. 概念

2. 栈的使用

2.1 方法

2.2 示例

3. 栈的模拟实现

4. 栈的应用场景

4.1 题目1:不可能的出栈序列

4.2 题目2:逆序打印单链表

4.3 题目3:逆波兰表达式求值

4.4 题目4:括号匹配

4.5 题目5:栈的压入、弹出训练

4.6 题目6:最小栈


1. 概念

(1)栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。

(2)进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。

(3)栈中的数据元素遵循后进先出的原则;

(4)压栈:栈的插入操作叫做进栈或压栈或入栈,入数据在栈顶;

         出栈:栈的删除操作叫做出栈,出数据在栈顶;

2. 栈的使用

2.1 方法

方法 功能
Stack() 构造一个空栈
E push(E e) 将e入栈并返回e
E pop() 将栈顶元素出栈并返回
E peek() 获取栈顶元素
int size() 获取栈中有效元素个数
boolean empty() 检测栈是否为空

2.2 示例

    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();
        for(int i=0;i<=5;i++){
            stack.push(i);  // 压栈
        }
        // 获取栈顶元素
        System.out.println(stack.peek());
        Integer a = stack.pop();  // 出栈栈顶元素
        System.out.println(a);
        System.out.println(stack.peek());
        System.out.println(stack.size());
    }

3. 栈的模拟实现

Stack继承于Vector,查Vector源码可知vector内部用数组实现,故而模拟实现采用数组栈;

(1)包类关系:

(2)MyStack:

package TestMyStack;

import java.util.Arrays;

public class MyStack {
    public int[] elem;
    public int useSize;
    public MyStack(){
        this.elem = new int[10];
    }
    // 压栈
    public void push(int val){
        if(isFull()){
            // 扩容
            reSize();
        }
        elem[useSize] = val;
        this.useSize++;
    }
    // 判满
    public boolean isFull(){
        if(elem.length == this.useSize){
            return true;
        }
        return false;
    }
    // 扩容
    public void reSize(){
        elem = Arrays.copyOf(elem, 2*elem.length);
    }
    // 出栈并返回出栈元素
    public int pop(){
        if(isEmpty()){
            throw new EmptyException();
        }
//        int val = elem[this.useSize-1];
//        this.useSize--;
//        return val;
        return this.elem[--this.useSize];
    }
    // 判空
    public boolean isEmpty(){
        return this.useSize==0;
    }
    // 获取栈顶元素
    public int peek(){
        return this.elem[this.useSize-1];
    }
}

(3)EmptyException:

package TestMyStack;

public class EmptyException extends RuntimeException{
    public EmptyException(){

    }
}

4. 栈的应用场景

4.1 题目1:不可能的出栈序列

若进栈序列为1,2,3,4,进栈过程中可以出栈,则不可能的出栈序列是:C
A. 1,4,3,2   B. 2,3,4,1   C.3,1,4,2    D.3,4,2,1

4.2 题目2:逆序打印单链表

(1)单链表的递归逆序打印法:

 public void fun1(ListNode pHead){
        // 递归逆序打印单链表
        if(pHead == null){
            return;
        }
        if(pHead.next == null){
            System.out.println(pHead.val);
        }
        fun1(pHead.next);
        System.out.println(pHead.val);
    }

(2)单链表的循环递归逆序打印法:

public void fun2(ListNode pHead){
        Stack<ListNode> stack = new Stack<>();
        ListNode cur = head;
        while(cur!=null){
            stack.push(cur);
            cur = cur.next;
        }
        // 遍历栈
        while(!stack.isEmpty()){
            ListNode top = stack.pop();
            System.out.print(top.val+" ");
        }
        System.out.println();
    }

4.3 题目3:逆波兰表达式求值

题目链接:150. 逆波兰表达式求值 - 力扣(LeetCode)

解题思路:遍历后缀算数表达式数组,若为数据则入栈,若为算术运算符则出栈栈顶两元素分别作为后操作数和前操作数,再将计算结果入栈,继续向后遍历数组,循环操作,直至栈中仅有一个元素,就是最终值;

代码:

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for(String x: tokens){
            if(!isOperation(x)){
                stack.push(Integer.parseInt(x));
            }else{
                int num2 = stack.pop();
                int num1 = stack.pop();
                switch(x){
                    case "+":
                        stack.push(num1+num2);
                        break;
                    case "-":
                        stack.push(num1-num2);
                        break;
                    case "*":
                        stack.push(num1*num2);
                        break;
                    case "/":
                        stack.push(num1/num2);
                        break;
                }
            }
        }
        return stack.pop();
    }
    private boolean isOperation(String x){
        if(x.equals("+")||x.equals("-")||x.equals("*")||x.equals("/")){
            return true;
        }
        return false;
    }
}

4.4 题目4:括号匹配

题目链接:20. 有效的括号 - 力扣(LeetCode)

代码:

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.empty()){
                    return false;  
                }
                //  第二种情况:栈不为空,判断是否匹配
                char ch2 = stack.peek();   //栈顶元素,必为左括号
                if(ch2 == '('&& ch == ')' || ch2 == '{' && ch == '}' || ch2 == '[' && ch == ']'){
                    stack.pop();
                }else{
                    return false;
                }
            }
        }
        if(!stack.empty()){
            return false;
        }
        return true;
    }
}

4.5 题目5:栈的压入、弹出训练

题目链接:栈的压入、弹出序列_牛客题霸_牛客网

解题思路:定义i下标遍历pushV,j下标遍历popV,将pushV的元素入栈,当二者元素不相等时,继续入栈pushV元素;当二者元素相等时,出栈栈顶元素,i和j均++;

代码:

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pushV int整型一维数组 
     * @param popV int整型一维数组 
     * @return bool布尔型
     */
    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(j<popV.length && !stack.empty() && stack.peek().equals(popV[j])){
                stack.pop();
                j++;
            }
        }
        return stack.empty();
    }
}

4.6 题目6:最小栈

题目链接:155. 最小栈 - 力扣(LeetCode)

解题思路:为目标栈开辟一个储存最小值的栈,依次遍历目标栈,将最小的元素入栈最小栈,最小栈栈顶元素就是目标栈的最小元素,且能在常数时间内检索到最小元素;

代码:

class MinStack {
    private Stack<Integer> stack;
    private Stack<Integer> minStack;
    public MinStack() {
        stack = new Stack<>();
        minStack = new Stack<>();
    }
    public void push(int val) {
        stack.push(val);
        if(minStack.empty()){
            minStack.push(val);
        }else{   //minStack不为空
            if(val <= minStack.peek()){  
                // 如果要插入的值比minStack的栈顶元素小
                minStack.push(val); 
                // 注意当val==minStack栈顶元素时,也需要将该元素入栈minStack
                // 因为stack出栈元素时,如果该元素是minStack中的元素,该元素也需要出栈
                // 以保证在出栈stack最小元素时,minStack中的最小元素不受影响

            }
        }
    }
    
    public void pop() {
        if(!stack.empty()){
            Integer val = stack.pop();
            if(val.equals(minStack.peek())){
                minStack.pop();
            }
            // 如果对元素拆箱为int类型,就可使用==进行判等
        }
    }
    
    public int top() {
       if(!stack.empty()){
           return stack.peek();
       }
       return -1;
    }
    
    public int getMin() {
        return minStack.peek();
    }
}

猜你喜欢

转载自blog.csdn.net/m0_63299495/article/details/131945572