栈(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. 线程安全考虑
-
非线程安全实现:
- 上述ArrayStack和LinkedStack都不是线程安全的
- 多线程环境下可能导致数据不一致
-
线程安全解决方案:
- 使用synchronized关键字修饰方法
- 使用java.util.concurrent包中的并发集合
- 推荐使用
ConcurrentLinkedDeque作为线程安全栈实现
5. 工程实践建议
- 优先使用Java标准库中的
Deque接口实现 - 避免使用已过时的
java.util.Stack类 - 栈大小限制:
- 数组实现有固定容量限制
- 链表实现受限于内存大小
- 异常处理:
- 空栈弹出操作应明确抛出异常
- 栈满情况处理
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. 栈的应用场景
- 函数调用栈
- 表达式求值
- 括号匹配
- 浏览器前进/后退
- 撤销操作
8. Java集合框架中的Stack类
Java提供了java.util.Stack类,但推荐使用Deque接口的实现类如ArrayDeque来代替Stack类。
9. 完整代码
本文中的代码示例可以在我的GitHub仓库中找到:查看完整代码