1. 简介
堆栈又名栈(stack),他是计算机科学中最基础的数据结构之一。可以算是一种受限制的线性结构,,具有后进先出(LIFO, Last In First Out)的特性。由于此特性,堆栈常用一维数组和链表等线性结构来实现。
2. 特点
栈的特点特别简单,先入后出,后入先出。
2.1 图解
栈的主要功能也很简单,无非就是出栈(pop)、入栈(push)和查看栈顶元素。由于这种限制,栈中元素的移除顺序与添加顺序相反,体现了后进先出的特性。
栈的重要术语:
- 栈顶:栈中允许进行插入和删除操作的端部。
- 栈底:与栈顶相对,是不能进行插入和删除操作的端部。
- 空栈:不含任何元素的栈。
栈的基本操作:
- 入栈:也称为压栈,是指在栈顶添加新的元素。
- 出栈:是指移除栈顶的元素,使其相邻的元素成为新的栈顶元素。
- 查看栈顶元素:此操作允许用户查看栈顶元素而不移除它。
2.2 结构
在java中,栈(Stack)的底层实现就是数组(Vector)。
数组实现的栈在尾部进行插入和删除操作时效率较高,而链表实现的栈虽然在空间使用上更加灵活,但需要更复杂的数据结构来支持高效的插入和删除操作。
jdk源码中Stack的实现也很简洁,如下图
主要的常用功能就三个:
public E push(E item) | 入栈,在栈顶添加元素 |
public synchronized E pop() | 出栈,返回栈顶元素并删除 |
public synchronized E peek() | 返回栈顶元素,但不删除 |
2.3 使用
public class StackExample {public static void main(String[] args) {// 创建一个空的栈对象Stack<Integer> stack = new Stack<>();// 向栈中压入元素stack.push(1);stack.push(2);stack.push(3);// 输出栈的大小System.out.println("栈的大小:" + stack.size());// 查看栈顶元素System.out.println("栈顶元素:" + stack.peek());// 弹出栈顶元素int topElement = stack.pop();System.out.println("弹出的元素:" + topElement);// 再次查看栈顶元素System.out.println("新的栈顶元素:" + stack.peek());// 判断栈是否为空System.out.println("栈是否为空:" + stack.isEmpty());}
}// 结果
栈的大小:3
栈顶元素:3
弹出的元素:3
新的栈顶元素:2
栈是否为空:false
3. 用途
- 表达式求值
- 逆波兰表达式求值:栈可以用于计算后缀表达式的值,通过遍历表达式,将数字压入栈中,遇到运算符则从栈中弹出相应数量的数字进行计算,再将结果压回栈中。
- 括号匹配
- 符号匹配:栈可以用于检查表达式中的括号是否匹配,通过遍历表达式,每当遇到左括号时,将其压入栈中;当遇到右括号时,判断栈顶元素是否为对应的左括号,并出栈。
- 算法辅助数据结构
- 深度优先搜索:在图形算法中,栈可以用于实现深度优先搜索,通过遍历节点并将未访问的邻接节点压入栈中,然后在后续的探索中继续这个过程。
- 历史记录
- 网页浏览器历史:浏览器的前进和后退功能可以通过两个栈来实现,一个栈用于存储已访问的页面,另一个栈用于存储回退的页面。
- 撤销操作
- 编辑器撤销:在文本编辑器中,每次编辑操作都可以被记录在一个栈中,当用户执行撤销操作时,可以从栈中取出最近的操作并执行相反的操作。
- 递归处理
- 递归转迭代:在处理递归问题时,可以使用栈来模拟递归调用的过程,将递归转换为迭代,从而避免栈溢出的风险。
- 函数调用堆栈
- 异常处理:在Java中,异常的传播和处理可以通过栈来实现,每个方法调用时的异常信息都会被记录在栈帧中,直到找到合适的异常处理器。
4. 总结
Java中的栈是一种非常重要的数据结构,它遵循后进先出(LIFO)的原则,即最后进入的元素最先被取出。栈在Java中的应用非常广泛,从基本的算法实现到深层次的程序执行机制,都离不开栈的支持。