栈与队列理论基础及训练
一.先有个印象他们是什么?
理论来说,先进先出的存储结构是队列,先进后出的存储结构是栈.
栈的例子:比如我们学习Java时的栈空间就是一个栈,这个栈空间中最先插入的栈帧是main函数的栈帧,然后main函数中调用新方法会插入一个新栈帧,这个新栈帧执行完了直接跳出栈空间,然后返回结果值到老栈帧调用方法的地方,这就是标准的先进后出,这也是为什么它叫栈空间吧!
队列的例子:比如下载一个东西不都是先进先下载,所以下载列表就是一个队列.
二.接下来卡哥给了四个栈问题,我们围绕这四个问题了解栈的底层(队列同理)
- C++中stack是容器么?
C++中栈和队列不是容器,栈和队列是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈的功能)。
所以STL中栈和队列往往不被归类为容器,而被归类为container adapter(容器适配器),它们对外提供一组统一的接口(如 push、pop、front、back 等),具体操作由底层容器完成,而底层容器是可插拔的.
在Java中,
Stack是一种容器类,它是java.util包的一部分,并且继承自Vector类,因此,它可以看作是Vector的一种特殊用法,具有后进先出(LIFO) 的特性,所以Java中栈被看作容器,这一点和C++不同.
- 我们使用的stack是属于哪个版本的STL?
C++标准库是有多个版本的,要知道我们使用的STL是哪个版本,才能知道对应的栈和队列的实现原理.SGI STL由Silicon Graphics Computer Systems公司参照HP STL实现,被Linux的C++编译器GCC所采用,SGI STL是开源软件,源码可读性甚高,是常用的版本.
Stack是Java标准库中的一部分,自JDK 1.0起就已经存在.因此,它属于Java最早期的集合框架.从 JDK 1.6 开始,推荐使用Deque(双端队列)来实现栈操作,因为它性能更好,设计更现代化,我们以后基本都用Deque实现栈和队列.
- 我们使用的STL(标准模板库)中stack是如何实现的?
栈和队列的底层容器可以选择不同的实现,如 deque、vector 或 list.主要是数组和链表,这也符合我们前面的认知,所有数据结构都是基于数组和链表实现的.
栈和队列在 SGI STL(C++ 标准模板库)中,默认使用 deque(双端队列) 作为底层实现,它既可以高效地在队首和队尾进行操作,也可以封住一端,仅开放另一端,模拟栈或队列的逻辑,相当于双端队列两边都可以进行操作,我们可以只用一边来模拟栈,两边都用来模拟队列:
- 作为栈:仅开放尾端(
push_back和pop_back)。 - 作为队列:开放首端(
push_front和pop_front)和尾端。
Java中我们说了用
Deque来实现栈和队列,实际上其实Java也是通过双端队列来模拟栈和队列.
Deque的底层实现可以是数组(ArrayDeque)或链表(LinkedList),具体实现取决于具体的类.具体来说,Deque是一个接口,它底下用数组实现的栈是ArrayDeque,用链表实现的栈是LinkedList,他们性能更好,然后我们创建栈的时候是:
Deque<Integer> stack = new ArrayDeque<>(); //用父类引用存了子类对象的指针
- stack提供迭代器来遍历stack空间么?
栈提供push和pop等等接口,但是栈不提供走访功能,也不提供迭代器(iterator).不像是set或者map 提供迭代器iterator来遍历所有元素.
Java中
Deque提供了迭代器,但是不建议用,因为栈和队列的设计核心是 一端操作元素(栈)或两端操作元素(队列).提供遍历功能(如通过迭代器访问所有元素)会破坏它们的抽象,用户可能试图对不符合 LIFO 或 FIFO 原则的元素进行操作.构造者希望用户专注于 栈和队列的特性操作(如
push、pop、offer、poll),而不是直接遍历.
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
public class DequeIteratorExample {
public static void main(String[] args) {
Deque<Integer> deque = new ArrayDeque<>();
deque.push(1);
deque.push(2);
deque.push(3);
System.out.println("通过迭代器遍历(从栈底到栈顶):");
Iterator<Integer> iterator = deque.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}