rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • 栈

  • 栈的核心特性
  • Java 栈的实现(数组版 + 链表版)
    • 1. 顺序栈(基于数组)
    • 2. 链式栈(基于链表)
    • 3. Java 内置栈(java.util.Stack)
  • 栈的经典算法题(附解析)
    • 1. 有效的括号(LeetCode 20)
    • 2. 最小栈(LeetCode 155)
    • 3. 每日温度(LeetCode 739)
    • 4. 逆波兰表达式求值(LeetCode 150)
    • 5. 柱状图中最大的矩形(LeetCode 84)
  • 总结
  • 资料

栈

栈(Stack)是一种遵循后进先出(LIFO,Last In First Out) 原则的线性数据结构,仅允许在一端(栈顶)进行插入和删除操作。以下从核心特性、Java 实现、经典算法题三个方面详解:

栈的核心特性

  1. 操作受限:仅允许在栈顶执行插入(push)和删除(pop),不支持随机访问。
  2. LIFO 原则:最后入栈的元素最先出栈(类似叠盘子,只能从最上面取放)。
  3. 实现方式:
    • 基于数组(顺序栈):内存连续,需预先指定容量,可能溢出。
    • 基于链表(链式栈):内存不连续,动态扩容,无溢出问题。
  4. 时间复杂度:push、pop、peek(查看栈顶元素)均为 O(1)。

Java 栈的实现(数组版 + 链表版)

1. 顺序栈(基于数组)

public class ArrayStack<T> {
    private T[] stack; // 存储元素的数组
    private int top;   // 栈顶指针(指向栈顶元素的索引)
    private int size;  // 栈的容量

    // 初始化栈
    @SuppressWarnings("unchecked")
    public ArrayStack(int capacity) {
        size = capacity;
        stack = (T[]) new Object[capacity];
        top = -1; // 初始栈空,指针为-1
    }

    // 入栈
    public void push(T value) {
        if (isFull()) {
            throw new RuntimeException("栈已满");
        }
        stack[++top] = value; // 指针先加1,再赋值
    }

    // 出栈(返回栈顶元素并删除)
    public T pop() {
        if (isEmpty()) {
            throw new RuntimeException("栈为空");
        }
        return stack[top--]; // 返回元素后指针减1
    }

    // 查看栈顶元素(不删除)
    public T peek() {
        if (isEmpty()) {
            throw new RuntimeException("栈为空");
        }
        return stack[top];
    }

    // 判断栈空
    public boolean isEmpty() {
        return top == -1;
    }

    // 判断栈满
    public boolean isFull() {
        return top == size - 1;
    }

    public static void main(String[] args) {
        ArrayStack<Integer> stack = new ArrayStack<>(5);
        stack.push(1);
        stack.push(2);
        System.out.println(stack.peek()); // 输出:2
        System.out.println(stack.pop());  // 输出:2
        System.out.println(stack.isEmpty()); // 输出:false
    }
}

2. 链式栈(基于链表)

public class LinkedStack<T> {
    // 节点类
    private static class Node<T> {
        T data;
        Node<T> next;
        Node(T data) {
            this.data = data;
        }
    }

    private Node<T> top; // 栈顶节点(头节点)
    private int size;    // 栈的大小

    // 入栈(在链表头部插入节点)
    public void push(T value) {
        Node<T> newNode = new Node<>(value);
        newNode.next = top; // 新节点指向原栈顶
        top = newNode;      // 更新栈顶为新节点
        size++;
    }

    // 出栈(删除并返回头节点)
    public T pop() {
        if (isEmpty()) {
            throw new RuntimeException("栈为空");
        }
        T data = top.data;
        top = top.next; // 栈顶指针后移
        size--;
        return data;
    }

    // 查看栈顶元素
    public T peek() {
        if (isEmpty()) {
            throw new RuntimeException("栈为空");
        }
        return top.data;
    }

    public boolean isEmpty() {
        return top == null;
    }

    public int size() {
        return size;
    }

    public static void main(String[] args) {
        LinkedStack<String> stack = new LinkedStack<>();
        stack.push("a");
        stack.push("b");
        System.out.println(stack.peek()); // 输出:b
        System.out.println(stack.pop());  // 输出:b
        System.out.println(stack.size()); // 输出:1
    }
}

3. Java 内置栈(java.util.Stack)

实际开发中可直接使用 JDK 提供的 Stack 类(继承自 Vector,基于数组实现),但更推荐 Deque 接口的 ArrayDeque(性能更优):

import java.util.ArrayDeque;
import java.util.Deque;

public class BuiltInStack {
    public static void main(String[] args) {
        // 推荐使用Deque作为栈(比Stack更高效)
        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(1);    // 入栈
        stack.push(2);
        System.out.println(stack.peek()); // 查看栈顶:2
        System.out.println(stack.pop());  // 出栈:2
        System.out.println(stack.isEmpty()); // false
    }
}

栈的经典算法题(附解析)

1. 有效的括号(LeetCode 20)

题目:判断字符串中的括号 ()[]{} 是否匹配(左右括号类型一致且嵌套正确)。

思路:左括号入栈,右括号与栈顶匹配,不匹配则无效。

public boolean isValid(String s) {
    Deque<Character> stack = new ArrayDeque<>();
    for (char c : s.toCharArray()) {
        if (c == '(') stack.push(')');
        else if (c == '[') stack.push(']');
        else if (c == '{') stack.push('}');
        // 右括号不匹配或栈空(多出来的右括号)
        else if (stack.isEmpty() || stack.pop() != c) {
            return false;
        }
    }
    // 栈空则所有左括号都匹配,否则有多余左括号
    return stack.isEmpty();
}

2. 最小栈(LeetCode 155)

题目:设计一个栈,支持 push、pop、top,并能在常数时间内检索到最小元素。

思路:用辅助栈存储当前最小值,与主栈同步操作。

class MinStack {
    private Deque<Integer> mainStack;
    private Deque<Integer> minStack; // 辅助栈,存储对应主栈状态的最小值

    public MinStack() {
        mainStack = new ArrayDeque<>();
        minStack = new ArrayDeque<>();
        minStack.push(Integer.MAX_VALUE); // 初始最小值为最大整数
    }

    public void push(int val) {
        mainStack.push(val);
        // 辅助栈压入当前最小值(val与栈顶最小值的较小者)
        minStack.push(Math.min(val, minStack.peek()));
    }

    public void pop() {
        mainStack.pop();
        minStack.pop(); // 同步弹出
    }

    public int top() {
        return mainStack.peek();
    }

    public int getMin() {
        return minStack.peek();
    }
}

3. 每日温度(LeetCode 739)

题目:给定温度数组,返回每个元素需要等待几天才会有更高的温度(若无则为 0)。

思路:单调栈(存储索引),栈内元素对应温度递减,遇到更高温度时计算天数。

public int[] dailyTemperatures(int[] temperatures) {
    int[] res = new int[temperatures.length];
    Deque<Integer> stack = new ArrayDeque<>(); // 存储温度的索引
    for (int i = 0; i < temperatures.length; i++) {
        // 当前温度 > 栈顶温度:弹出并计算天数
        while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
            int prevIndex = stack.pop();
            res[prevIndex] = i - prevIndex;
        }
        stack.push(i); // 当前索引入栈
    }
    return res; // 未弹出的索引对应res为0(默认值)
}

4. 逆波兰表达式求值(LeetCode 150)

题目:计算逆波兰表达式(后缀表达式)的值,如 ["2","1","+","3","*"] 结果为 9。

思路:数字入栈,遇到运算符则弹出两个数字计算,结果入栈。

public int evalRPN(String[] tokens) {
    Deque<Integer> stack = new ArrayDeque<>();
    for (String token : tokens) {
        if ("+".equals(token)) {
            stack.push(stack.pop() + stack.pop());
        } else if ("-".equals(token)) {
            int b = stack.pop(), a = stack.pop(); // 注意顺序:a - b
            stack.push(a - b);
        } else if ("*".equals(token)) {
            stack.push(stack.pop() * stack.pop());
        } else if ("/".equals(token)) {
            int b = stack.pop(), a = stack.pop(); // a / b
            stack.push(a / b);
        } else {
            stack.push(Integer.parseInt(token)); // 数字入栈
        }
    }
    return stack.pop();
}

5. 柱状图中最大的矩形(LeetCode 84)

题目:给定柱状图高度数组,求能勾勒出的最大矩形面积。

思路:单调栈(存储索引),找到每个柱子左右第一个更矮的柱子,计算以当前柱子为高的最大宽度。

public int largestRectangleArea(int[] heights) {
    int n = heights.length;
    int[] left = new int[n];  // 左边界:第一个比当前矮的索引
    int[] right = new int[n]; // 右边界:第一个比当前矮的索引
    Deque<Integer> stack = new ArrayDeque<>();

    // 计算左边界
    for (int i = 0; i < n; i++) {
        while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {
            stack.pop();
        }
        left[i] = stack.isEmpty() ? -1 : stack.peek();
        stack.push(i);
    }

    stack.clear(); // 清空栈用于计算右边界
    // 计算右边界
    for (int i = n - 1; i >= 0; i--) {
        while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) {
            stack.pop();
        }
        right[i] = stack.isEmpty() ? n : stack.peek();
        stack.push(i);
    }

    // 计算最大面积:height[i] * (right[i] - left[i] - 1)
    int maxArea = 0;
    for (int i = 0; i < n; i++) {
        maxArea = Math.max(maxArea, heights[i] * (right[i] - left[i] - 1));
    }
    return maxArea;
}

总结

栈的核心是 LIFO 原则,其特性使其在以下场景中不可或缺:

  • 括号匹配、表达式求值(语法解析);
  • 单调栈优化(解决 “找左右第一个更大 / 更小元素” 类问题);
  • 深度优先搜索(DFS)、回溯算法(递归本质是栈)。

掌握栈的实现原理和单调栈技巧,能高效解决各类线性结构的算法题。实际开发中,优先使用 ArrayDeque 作为栈(性能优于 Stack),避免线程安全的 Vector 带来的开销。

资料

数据结构之单调栈
单调栈解题模板
Leetcode 单调栈问题总结
[动画:什么是单调栈?](https://mp.weixin.qq.com/s?__biz=MzUyNjQxNjYyMg==&mid=2247486327&idx=2&sn=12898a14bb5eca6508c9edb785b79d18&chksm=fa0e64f6cd79ede0cd08cf2059bc848b879918c2507b80d785409b49a9dbb6909b64dd146ff5&scene=21#wechat_redirect# 栈)
数据结构之单调栈
单调栈解题模板

最近更新:: 2025/10/27 23:01
Contributors: luokaiwen