一、什么是栈
栈(Stack)是一种基础且重要的数据结构,它在计算机科学和软件开发中有着广泛的应用。栈是一个遵循“后进先出”(Last In First Out, LIFO;类似于弹匣)原则的线性表,这意味着最后被压入栈中的元素将是第一个被弹出的元素。
二、栈能做什么
1、函数调用栈(Call Stack):
- 在程序执行过程中,每当一个函数被调用时,其局部变量、返回地址等信息会被压入栈中形成一个栈帧,用于保存函数运行状态。当函数执行完毕时,相应的栈帧从栈顶弹出,程序控制权返回到上一级调用。
2、表达式求值与计算:
- 栈可以用来辅助计算逆波兰表示法(Reverse Polish Notation,RPN)等非传统格式的数学表达式。
- 也可以用于括号匹配问题,如检查字符串中的括号是否正确嵌套。
3、深度优先搜索(DFS):
- 在图或树的遍历算法中,栈用于实现深度优先搜索策略,通过不断将下一个待访问节点压入栈来决定搜索顺序。
4、数制转换:
- 进制转换(例如十进制转二进制、八进制或十六进制)可以通过反复除以目标基数并把余数压入栈来完成,最后栈中的元素序列即为目标进制下的数字。
5、内存管理:
- 在某些内存分配方案中,栈可用于为函数自动分配和释放本地内存空间。
6、操作系统任务调度:
- 操作系统内核可能会使用栈来维护进程或线程的上下文切换信息。
7、回溯算法:
- 在解决一些组合优化问题时,栈常常作为临时存储路径的一部分,帮助算法在尝试失败时快速恢复到之前的决策点。
8、递归函数的实现:
- 虽然递归本身不是直接依赖于栈,但在编译器内部处理递归调用时会利用栈来保存递归层级的信息。
总之,栈是一种灵活且高效的工具,它的特点是操作简单且符合很多编程场景对“后进先出”逻辑的需求。
三、实现方式
栈可以通过两种主要的方式来实现:顺序存储(数组)和 链式存储(链表)。
1、数组
- 在数组中,栈的元素从固定的一端(通常是数组的末尾)进行插入和删除。
- 初始化一个固定大小或动态可扩展的数组。
- 定义一个变量 top 来表示栈顶的位置。当栈为空时,top 为 -1 或者指向数组的第一个位置之外;随着元素入栈,top 向后移动一位;出栈时,top 回退一位。
- 数组实现的优点是访问速度快,由于只在一端操作,入栈和出栈的时间复杂度通常为 O(1)。
- 缺点是在预先分配的空间用完后需要扩容,扩容操作可能涉及到数据迁移,效率相对较低,并且可能会造成一定的空间浪费。
2、链表
- 使用单链表作为底层结构,栈底可以看作是链表的尾部,而栈顶则是链表头部。
- 新元素入栈时,在链表头部添加新的节点。
- 出栈时,只需删除链表的头部节点即可。
- 链表实现的优点是可以灵活地扩展栈的容量,无需预先确定栈的大小,而且不会遇到数组扩容的问题。
- 缺点是相比于数组,链表的插入和删除操作虽然也是 O(1),但通常涉及指针操作,因此在实际运行时可能会有额外的开销。
四、代码案例
1、定义一个公共接口
package stack;
public interface Stack<E> {
/**
* 入栈:将一个元素添加到栈顶。
* @param value 值
* @return 压入成功返回 true, 否则返回 false
*/
boolean push(E value);
/**
* 出栈:移除并返回栈顶的元素。
*
* @return 栈非空返回栈顶元素, 栈为空返回 null
*/
E pop();
/**
* 查看栈顶元素:不移除栈顶元素的情况下查看其值。
*
* @return 栈非空返回栈顶元素, 栈为空返回 null
*/
E peek();
/**
* 判断是否为空栈:检查栈中是否包含元素。
*
* @return 空返回 true, 否则返回 false
*/
boolean isEmpty();
/**
* 获取栈大小:获取栈内元素的数量。
*
* @return 返回个数
*/
int size();
/**
* 判断栈是否已满
* @return 满返回 true, 否则返回 false
*/
boolean isFull();
}
2、数组实现案例
package stack;
/**
* 数组实现栈
*
* @param <E>
*/
public class ArrayStack<E> implements Stack {
//数组栈
private final E[] array;
//top
private int top = 0;
//栈内元素的数量
private int size = 0;
public ArrayStack(int length) {
this.array = (E[]) new Object[length];
}
/**
* 入栈:将一个元素添加到栈顶。
*
* @param value 值
* @return
*/
@Override
public boolean push(Object value) {
//判断栈是否已满
if (isFull()) {
return false;
}
array[top] = (E) value;
top++;
size++;
return true;
}
/**
* 出栈:移除并返回栈顶的元素。
*
* @return 栈非空返回栈顶元素, 栈为空返回 null
*/
@Override
public Object pop() {
//判断数组是否为空。
if (isEmpty()) {
return null;
}
E data = array[top - 1];
array[top - 1] = null;//清空出栈的元素,垃圾回收
top--;
size--;
return data;
}
/**
* 查看栈顶元素:不移除栈顶元素的情况下查看其值。
*
* @return 栈非空返回栈顶元素, 栈为空返回 null
*/
@Override
public Object peek() {
//判断数组是否为空。
if (isEmpty()) {
return null;
}
E data = array[top - 1];
return data;
}
/**
* 判断是否为空栈:检查栈中是否包含元素。
*
* @return 空返回 true, 否则返回 false
*/
@Override
public boolean isEmpty() {
return top == 0;
}
/**
* 获取栈大小:获取栈内元素的数量。
*
* @return 返回个数
*/
@Override
public int size() {
return size;
}
/**
* 判断栈是否已满
*
* @return 满返回 true, 否则返回 false
*/
@Override
public boolean isFull() {
return size == array.length;
}
//返回队列中的内容
public E[] getArray() {
return array;
}
}
3、链表实现案例
package stack;
public class LinkedListStack<E> implements Stack {
//栈的长度
private int length;
//栈内元素的数量
private int size;
private Node head;
public LinkedListStack(int length) {
this.length = length;
this.size = 0;
this.head = new Node(null, null);
}
class Node {
E value;
Node next;
public Node(E value, Node node) {
this.value = value;
this.next = node;
}
}
/**
* 入栈:将一个元素添加到栈顶。
*
* @param value 值
* @return 压入成功返回 true, 否则返回 false
*/
@Override
public boolean push(Object value) {
//判断栈是否已满
if (isFull()) {
return false;
}
head.next = new Node((E) value, head.next);
size++;
return true;
}
/**
* 出栈:移除并返回栈顶的元素。
*
* @return 栈非空返回栈顶元素, 栈为空返回 null
*/
@Override
public Object pop() {
if (isEmpty()) {
return null;
}
Node data = head.next;
head.next = data.next;
size--;
return data.value;
}
/**
* 查看栈顶元素:不移除栈顶元素的情况下查看其值。
*
* @return 栈非空返回栈顶元素, 栈为空返回 null
*/
@Override
public Object peek() {
if (isEmpty()) {
return null;
}
return head.next.value;
}
/**
* 判断是否为空栈:检查栈中是否包含元素。
*
* @return 空返回 true, 否则返回 false
*/
@Override
public boolean isEmpty() {
return head.next == null;
}
/**
* 获取栈大小:获取栈内元素的数量。
*
* @return 返回个数
*/
@Override
public int size() {
return size;
}
/**
* 判断栈是否已满
*
* @return 满返回 true, 否则返回 false
*/
@Override
public boolean isFull() {
return size == length;
}
}