链表实现
🔑栈顶指针指向最新(头)元素,如果空栈,head==null。
所有pop peek push都需要分两种情况(空/非空栈)讨论。
图
[pic from link]
注释
// 定义一个泛型栈,T是泛型类型参数
class ListStack<T> {
// 内部节点类,用于链表结构
class Node<T> {
public T t; // 节点存储的数据
public Node next; // 指向下一个节点的指针
}
public Node<T> head; // 栈顶指针,初始化时指向链表的头节点
// 构造方法,初始化一个空栈
ListStack() {
head = null;
}
// 入栈方法,将元素t推入栈顶
public void push(T t) {
// 如果传入的元素是null,抛出空指针异常
if (t == null) {
throw new NullPointerException("参数不能为null");
}
// 如果栈为空,即头节点为null,创建新节点作为头节点
if (head == null) {
head = new Node<T>(); // 创建新节点
head.t = t; // 设置节点数据
head.next = null; // 新节点的下一个节点为null
} else {
// 如果栈不为空,创建新节点并将其链接到链表前端
Node<T> temp = head; // 临时保存当前头节点
head = new Node<>(); // 创建新的头节点
head.t = t; // 设置新节点的数据
head.next = temp; // 新节点指向原来的头节点
}
}
// 出栈方法,返回并移除栈顶元素
public T pop() {
// 如果栈为空,则返回null
if (head == null) {
return null;
}
T t = head.t; // 临时保存栈顶元素的数据
head = head.next; // 将头节点指针移动到下一个节点,即移除当前栈顶
return t; // 返回栈顶元素的数据
}
// 查看栈顶元素,但不从栈中移除它
public T peek() {
// 如果栈为空,则返回null
if (head == null) {
return null;
}
return head.t; // 返回栈顶元素的数据,不移除元素
}
// 检查栈是否为空
public boolean isEmpty() {
// 如果头节点为null,表示栈为空
return head == null;
}
}
数组实现
top 指向下一个可用vs.最新
选择top
指针指向最新元素还是下一个可用位置,通常取决于个人偏好和具体情况,因为功能上它们是等效的。然而,每种方式都有其特定的优缺点:
-
指向最新元素(Top points to the last element) :
-
优点:
pop()
操作简单,直接返回stack[top]
,然后top--
。- 更直观地表示栈顶元素的位置。
-
缺点:
- 必须先增加
top
,然后再进行push()
操作。 - 栈空时需要处理
top
为-1
的情况。
- 必须先增加
-
-
指向下一个可用位置(Top points to the next available position) :
-
优点:
push()
操作简单,先将元素赋给stack[top]
,然后top++
。top
的初始值为0
,这在某些情况下可能更符合从0开始的数组索引习惯。
-
缺点:
pop()
操作前需要先减少top
。top
的值等于数组长度时,表示栈满,可能需要额外逻辑来处理。
-
一般来说,在计算机科学中,栈通常被视为从0开始索引的,因此第二种方法(top
指向下一个可用位置)在教科书和示例代码中更常见。这种方法的isEmpty()
实现也更直观,因为当top == 0
时,很明显栈是空的。
图
注释
import java.util.Arrays;
// 泛型栈的定义,可以存储任意类型的对象
class Mystack<T> {
// 栈元素存储数组
private Object[] stack;
// 栈顶指针,指向下一个可用位置
private int top;
// 构造器,初始化栈的默认大小为10
Mystack() {
stack = new Object[10]; // 初始化数组大小为10
top = 0; // 初始化栈顶指针为0
}
// 检查栈是否为空
public boolean isEmpty() {
return top == 0; // 如果栈顶指针为0,栈为空
}
// 查看栈顶元素但不移除
public T peek() {
T t = null; // 初始化返回元素为null
if (top > 0) { // 如果栈不为空
t = (T) stack[top - 1]; // 取出栈顶元素,但不修改栈顶指针
}
return t; // 返回栈顶元素
}
// 元素入栈
public void push(T t) {
expandCapacity(top + 1); // 确保容量足够
stack[top] = t; // 将元素放在栈顶指针指向的位置
top++; // 栈顶指针递增
}
// 元素出栈
public T pop() {
T t = peek(); // 使用peek()方法获取栈顶元素
if (top > 0) { // 如果栈不为空
stack[top - 1] = null; // 清除栈顶元素
top--; // 栈顶指针递减
}
return t; // 返回栈顶元素
}
// 扩展栈的容量
public void expandCapacity(int size) {
int len = stack.length; // 当前栈数组的长度
if (size > len) { // 如果所需容量大于当前长度
size = size * 3 / 2 + 1; // 扩容策略:当前需求大小的1.5倍再加1
stack = Arrays.copyOf(stack, size); // 使用Arrays.copyOf进行数组扩容
}
}
}