1. 什么是栈?
- 典型的“栈”结构:后进者先出,先进者后出。
- 从栈的操作特性来看,是一种
“操作受限”的线性表
,只允许在一端插入和删除数据,只支持入栈 push()和出栈 pop()操作。
2. 为什么需要栈?
- 栈是一种操作受限的数据结构,其操作特性用数组和链表均可实现。
- 特定的数据结构是对特定场景的抽象,而且,数组或链表暴露了太多的操作接口,操作上的确灵活自由,但使用时就比较不可控,自然也就更容易出错。
- 所以,当某个数据集合只涉及在某端插入和删除数据,且满足后进者先出,先进者后出的操作特性时,我们应该首选栈这种数据结构。
3. 如何实现栈?
- 栈既可以用数组来实现,也可以用链表来实现。用数组实现的栈,叫作顺序栈,用链表实现的栈,叫作链式栈。
- 不管基于数组还是链表,入栈、出栈的时间复杂度都为 O(1)。
不管是顺序栈还是链式栈,存储数据只需要一个大小为 n 的数组就够了。
在入栈和出栈过程中,只需要一两个临时变量存储空间,所以空间复杂度是 O(1)。
入栈、出栈只涉及栈顶个别数据的操作,所以时间复杂度都是 O(1)。
- 栈的API
public class Stack<Item> {
public void push(Item item){
}
public Item pop(){
}
public boolean isEmpty(){
}
public int size(){
}
public Item peek(){
}
}
- 支持动态扩容的顺序栈(数组实现)
思路:如果要实现一个支持动态扩容的栈,我们只需要底层依赖一个支持动态扩容的数组就可以了。当栈满了之后,我们就申请一个更大的数组,将原来的数据搬移到新数组中。
出栈的时间复杂度是 O(1)。
入栈操作,最好情况时间复杂度是 O(1),最坏情况时间复杂度是 O(n)。均摊时间复杂度为O(1)。
时间复杂度分析:根据均摊复杂度的定义,可以得数组实现(自动扩容)符合大多数情况是O(1)级别复杂度,个别情况是O(n)级别复杂度,比如自动扩容时,会进行完整数据的拷贝。一般来说,均摊复杂度等于最好情况复杂度
。
空间复杂度分析:在入栈和出栈的过程中,只需要一两个临时变量存储空间,所以O(1)级别。我们说空间复杂度的时候,是指除了原本的数据存储空间外,算法运行还需要额外的存储空间。
实现代码:
public class StackOfArray<Item> implements Iterable<Item>{
Item[] a = (Item[])new Object[1];
int N = 0;
public StackOfArray