栈是什么?
一种先进后出,后进先出的数据结构,只允许在一端插入和删除数据。
数组或链表与栈对比
数组或链表操作灵活自由,但是暴露了太多的操作接口,使用时比较不可控,容易出错。
栈使用受限较多,但是比较可控,不容易出错。
“栈”操作的时间、空间复杂度
不管是 顺序栈(基于数组实现的栈) 还是 链式栈(基于链表实现的栈),我们存储数据只需要一个大小为 n 的数组就够了,在入栈和出栈过程中,只需要一两个临时变量存储空间,所以空间复杂度为 O(1)。
不管是 顺序栈 还是 链式栈,入栈、出栈都只涉及栈顶个别数据的操作,所以时间复杂度为 O(1)。
栈支持动态扩容
链式栈自然不必说,顺序栈也是支持动态扩容的,只需要底层依赖一个支持动态扩容的数组即可。
虽然支持动态扩容的栈在开发中不常用到,但是借用它可以更好地进行栈的复杂度分析。
以入栈操作举个例子,当栈中有空闲空间时,入栈操作的时间复杂度为 O(1);当空间不够使,就需要重新申请内存和数据迁移,所以时间复杂度就变成了 O(n)。
因为大部分情况下,入栈操作的时间复杂度都为 O(1),只有在极端情况下才是 O(n),根据前面所讲的均摊时间复杂度,将耗时多的入栈操作的时间均摊到其他入栈操作上,平均情况下的耗时就接近 O(1),因此栈的时间复杂度为 O(1)。
实现一个简单的顺序栈
1 // 基于数组实现的顺序栈 2 public class ArrayStack { 3 private String[] items; // 数组 4 private int count; // 栈中元素个数 5 private int n; // 栈的大小 6 7 // 初始化数组,申请一个大小为 n 的数组空间 8 public ArrayStack(int n) { 9 this.items = new String[n]; 10 this.n = n; 11 this.count = 0; 12 } 13 14 // 入栈操作 15 public boolean push(String item) { 16 // 数组空间不够了,直接返回 false,入栈失败。 17 if (count == n) { 18 return false; 19 } 20 // 将 item 放到下标为 count 的位置,并且 count 加一 21 items[count] = item; 22 ++count; 23 return true; 24 } 25 26 // 出栈操作 27 public String pop() { 28 // 栈为空,则直接返回 null 29 if (count == 0) { 30 return null; 31 } 32 // 返回下标为 count-1 的数组元素,并且栈中元素个数 count 减一 33 String tmp = items[count-1]; 34 --count; 35 return tmp; 36 } 37 }
内容小结
- 栈是一种操作受限的数据结构,只支持入栈和出栈操作。
- 栈最大的特点是后进先出。
- 栈既可以通过数组实现,也可以通过链表实现。
- 不管是数组还是链表,入栈、出栈的时间复杂度都是 O(1)。