栈的数据结构与Java实现

60 阅读3分钟

栈(Stack)的数据结构与Java实现

1. 栈的基本概念

栈是一种遵循"后进先出"(LIFO)原则的线性数据结构。它只允许在栈顶进行插入(push)和删除(pop)操作。

2. Java中的栈实现

2.1 基于数组的实现

public class ArrayStack {
    private int maxSize;
    private int[] stackArray;
    private int top;

    public ArrayStack(int size) {
        this.maxSize = size;
        this.stackArray = new int[maxSize];
        this.top = -1;
    }

    /**
 * 将元素压入栈顶
 * @param value 要压入的元素
 * @throws RuntimeException 当栈已满时抛出异常
 */
    public void push(int value) {
        if (isFull()) {
            throw new RuntimeException("Stack is full");
        }
        stackArray[++top] = value;
    }

    /**
 * 弹出并返回栈顶元素
 * @return 栈顶元素
 * @throws RuntimeException 当栈为空时抛出异常
 */
    public int pop() {
        if (isEmpty()) {
            throw new RuntimeException("Stack is empty");
        }
        return stackArray[top--];
    }

    /**
 * 查看栈顶元素但不移除
 * @return 栈顶元素
 * @throws RuntimeException 当栈为空时抛出异常
 */
    public int peek() {
        if (isEmpty()) {
            throw new RuntimeException("Stack is empty");
        }
        return stackArray[top];
    }

    public boolean isEmpty() {
        return top == -1;
    }

    public boolean isFull() {
        return top == maxSize - 1;
    }
}

2.2 基于链表的实现

public class LinkedStack {
    private static class Node {
        int data;
        Node next;

        Node(int data) {
            this.data = data;
        }
    }

    private Node top;

    public void push(int data) {
        Node newNode = new Node(data);
        newNode.next = top;
        top = newNode;
    }

    /**
 * 弹出并返回栈顶元素
 * @return 栈顶元素
 * @throws RuntimeException 当栈为空时抛出异常
 */
    public int pop() {
        if (isEmpty()) {
            throw new RuntimeException("Stack is empty");
        }
        int data = top.data;
        top = top.next;
        return data;
    }

    /**
 * 查看栈顶元素但不移除
 * @return 栈顶元素
 * @throws RuntimeException 当栈为空时抛出异常
 */
public int peek() {
        if (isEmpty()) {
            throw new RuntimeException("Stack is empty");
        }
        return top.data;
    }

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

3. 时间复杂度分析

  • 基本操作时间复杂度:

    • push: O(1)
    • pop: O(1)
    • peek: O(1)
    • isEmpty: O(1)
  • 空间复杂度: O(n)

4. 线程安全考虑

  1. 非线程安全实现:

    • 上述ArrayStack和LinkedStack都不是线程安全的
    • 多线程环境下可能导致数据不一致
  2. 线程安全解决方案:

    • 使用synchronized关键字修饰方法
    • 使用java.util.concurrent包中的并发集合
    • 推荐使用ConcurrentLinkedDeque作为线程安全栈实现

5. 工程实践建议

  1. 优先使用Java标准库中的Deque接口实现
  2. 避免使用已过时的java.util.Stack
  3. 栈大小限制:
    • 数组实现有固定容量限制
    • 链表实现受限于内存大小
  4. 异常处理:
    • 空栈弹出操作应明确抛出异常
    • 栈满情况处理

6. 性能对比测试

public class StackBenchmark {
    public static void main(String[] args) {
        int size = 10000000;
        
        // ArrayStack测试
        long start = System.currentTimeMillis();
        ArrayStack arrayStack = new ArrayStack(size);
        for (int i = 0; i < size; i++) {
            arrayStack.push(i);
        }
        for (int i = 0; i < size; i++) {
            arrayStack.pop();
        }
        System.out.println("ArrayStack耗时: " + (System.currentTimeMillis() - start) + "ms");
        
        // LinkedStack测试
        start = System.currentTimeMillis();
        LinkedStack linkedStack = new LinkedStack();
        for (int i = 0; i < size; i++) {
            linkedStack.push(i);
        }
        for (int i = 0; i < size; i++) {
            linkedStack.pop();
        }
        System.out.println("LinkedStack耗时: " + (System.currentTimeMillis() - start) + "ms");
        
        // ArrayDeque测试
        start = System.currentTimeMillis();
        Deque<Integer> arrayDeque = new ArrayDeque<>(size);
        for (int i = 0; i < size; i++) {
            arrayDeque.push(i);
        }
        for (int i = 0; i < size; i++) {
            arrayDeque.pop();
        }
        System.out.println("ArrayDeque耗时: " + (System.currentTimeMillis() - start) + "ms");
    }
}

7. 栈的应用场景

  1. 函数调用栈
  2. 表达式求值
  3. 括号匹配
  4. 浏览器前进/后退
  5. 撤销操作

8. Java集合框架中的Stack类

Java提供了java.util.Stack类,但推荐使用Deque接口的实现类如ArrayDeque来代替Stack类。

9. 完整代码

本文中的代码示例可以在我的GitHub仓库中找到:查看完整代码