第4课_栈的实现
热度🔥:107 免费课程
授课语音
实现栈的算法设计
栈(Stack)是一种常用的数据结构,它遵循“后进先出”(LIFO, Last In, First Out)的原则。也就是说,最后插入的元素最先被取出。在实现栈时,我们通常希望栈的操作,如 push
和 pop
,能够在常数时间内完成。本文将介绍如何设计一个支持常数时间 push
和 pop
操作的栈。
1. 栈的基本操作
栈通常支持以下基本操作:
- push(e):将元素
e
压入栈顶。 - pop():将栈顶元素弹出,并返回该元素。
- top():返回栈顶元素,但不弹出。
- isEmpty():检查栈是否为空。
- size():返回栈中元素的个数。
我们的目标是实现 push
和 pop
操作的时间复杂度为 O(1)
,即常数时间。
2. 设计思路
为了实现常数时间的 push
和 pop
操作,我们可以使用两个栈来解决。具体设计思路如下:
- 主栈(stack1):用于存储栈的元素。所有的
push
操作都发生在主栈中。 - 辅助栈(stack2):用于辅助
pop
操作。当我们从主栈中弹出元素时,如果辅助栈为空,则将主栈中的元素全部倒入辅助栈中。这样,辅助栈的顶部就是最先进入主栈的元素,实现了栈的后进先出特性。
关键点
- push 操作:直接将元素压入主栈
stack1
。 - pop 操作:如果辅助栈为空,先将主栈的所有元素倒入辅助栈。然后从辅助栈中弹出元素。
通过这种设计,我们可以保证 push
操作始终是常数时间 O(1)
,而 pop
操作平均也是常数时间 O(1)
,尽管在某些情况下 pop
操作需要将主栈的元素转移到辅助栈中,但这可以平均分摊到多个操作中。
3. 代码实现
import java.util.Stack;
public class MyStack {
// 主栈,用于存储元素
private Stack<Integer> stack1;
// 辅助栈,用于帮助pop操作
private Stack<Integer> stack2;
// 构造函数,初始化栈
public MyStack() {
stack1 = new Stack<>();
stack2 = new Stack<>();
}
// push操作:将元素压入栈顶
public void push(int x) {
stack1.push(x); // 直接压入主栈
}
// pop操作:从栈顶弹出元素
public int pop() {
// 如果辅助栈为空,将主栈的元素倒入辅助栈
if (stack2.isEmpty()) {
while (!stack1.isEmpty()) {
stack2.push(stack1.pop()); // 主栈元素倒入辅助栈
}
}
// 弹出辅助栈的元素
return stack2.pop();
}
// top操作:获取栈顶元素,但不弹出
public int top() {
// 如果辅助栈为空,将主栈的元素倒入辅助栈
if (stack2.isEmpty()) {
while (!stack1.isEmpty()) {
stack2.push(stack1.pop()); // 主栈元素倒入辅助栈
}
}
// 返回辅助栈的栈顶元素
return stack2.peek();
}
// isEmpty操作:检查栈是否为空
public boolean isEmpty() {
return stack1.isEmpty() && stack2.isEmpty();
}
// size操作:返回栈中元素的个数
public int size() {
return stack1.size() + stack2.size();
}
}
4. 详细中文注释
构造函数:
public MyStack()
初始化主栈stack1
和辅助栈stack2
。push(int x)
将元素x
压入主栈stack1
。pop()
如果辅助栈为空,则将主栈中的所有元素倒入辅助栈,这样辅助栈的栈顶就是主栈最早压入的元素。然后从辅助栈弹出栈顶元素。top()
与pop()
操作类似,先将主栈元素转移到辅助栈,然后返回辅助栈的栈顶元素。isEmpty()
检查主栈和辅助栈是否都为空,如果都为空,则栈为空。size()
返回主栈和辅助栈中元素的总数。
5. 复杂度分析
时间复杂度:
push(x)
:每次操作都在主栈中进行,时间复杂度是O(1)
。pop()
:如果辅助栈为空,则将主栈的元素倒入辅助栈,这个操作的时间复杂度是O(n)
(假设主栈中有n
个元素),但是每个元素只会被移动一次,所以平均时间复杂度是O(1)
。top()
:与pop()
操作类似,平均时间复杂度是O(1)
。
空间复杂度:
O(n)
,其中n
是栈中元素的个数,因为栈中所有元素都需要存储在主栈和辅助栈中。
6. 总结
通过使用两个栈,我们设计了一种能够在常数时间内执行 push
操作,并且在平均常数时间内执行 pop
和 top
操作的栈。这个设计方案在实际使用中能够高效地处理栈的基本操作,适合需要频繁进行栈操作的场景。