栈与队列理论要点
1. C++中stack 是容器么?
栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(可以控制使用不同容器来实现栈的功能)。所以STL中栈往往不被归类为容器,而被归类为container adapter(容器适配器)。
2. 我们使用的stack是属于哪个版本的STL?
三个普遍的STL版本:
HP STL 其他版本的C++ STL,一般是以HP STL为蓝本实现出来的,HP STL是C++ STL的第一个实现版本,而且开放源代码。
P.J.Plauger STL 由P.J.Plauger参照HP STL实现出来的,被Visual C++编译器所采用,不是开源的。
SGI STL 由Silicon Graphics Computer Systems公司参照HP STL实现,被Linux的C++编译器GCC所采用,SGI STL是开源软件,源码可读性甚高。
本文介绍的栈和队列也是SGI STL里面的数据结构
3. 我们使用的STL中stack是如何实现的?
栈的内部结构,栈的底层实现可以是vector,deque,list 都是可以的, 主要就是数组和链表的底层实现。
我们常用的SGI STL,默认是以deque为栈的底层结构。deque是一个双向队列,只要封住一段,只开通另一端就可以实现栈的逻辑了。
4. stack 提供迭代器来遍历stack空间么?
栈提供 push 和 pop 等接口,所有元素必须符合先进后出规则,所以栈不提供走访功能,也不提供迭代器(iterator)。 不像是set 或者map 提供迭代器iterator来遍历所有元素。
- 以上对队列的数据结构也完全相同
232. 用栈实现队列
题目链接:Leetcode232. 用栈实现队列
文章讲解:代码随想录—232. 用栈实现队列
思路:
因为栈是单向封闭的,因此需要使用两个栈分别模拟输入和输出,能使用的函数只有 pop、push、empty这些,概念问题比较简单。
其中 peek 的实现用到了 pop 的实现,因此在这里进行了复用,建议平常也善用复用的函数。
class MyQueue {
public:
stack<int> stin;
stack<int> stout;
MyQueue() {
}
void push(int x) {
stin.push(x);
}
int pop() {
if (stout.empty()) {
while (!stin.empty()) {
stout.push(stin.top());
stin.pop();
}
}
int res = stout.top();
stout.pop();
return res;
}
int peek() {
int res = this->pop();
stout.push(res);
return res;
}
bool empty() {
return stin.empty() && stout.empty();
}
};
时间复杂度: push和empty为O(1), pop和peek为O(n)
空间复杂度: O(n)
225. 用队列实现栈
题目链接:Leetcode225. 用队列实现栈
文章讲解:代码随想录—225. 用队列实现栈
思路:
可以像上一题一样建立两个队列实现进栈和出栈,但转来转去太麻烦了。讲解中的优化方案只用一个队列就实现了这些操作,当出栈时只需要把除了队尾最后一个元素外的所有元素重新插进队列,再从头部弹出的就是最后进队的“栈头”元素。
class MyStack {
public:
queue<int> que;
MyStack() {
}
void push(int x) {
que.push(x);
}
int pop() {
int size = que.size();
while (-- size) {
que.push(que.front());
que.pop();
}
int res = que.front();
que.pop();
return res;
}
int top() {
return que.back();
}
bool empty() {
return que.empty();
}
};
时间复杂度: pop为O(n),其他为O(1)
空间复杂度: O(n)
拓展
栈包含的函数:pop()、push()、empty()、size()、top()
队列包含的函数:pop()、push()、empty()、size()、front()、back()
总结
今天的两个题都是简单的概念题,理解好栈和队列的定义就能很容易写出代码。注意要活用复用代码,很多时候会事半功倍。