Day10:
理论基础
栈和队列的原理:栈是先进后出,队列是先进先出,
思考以下问题:
- C++中stack 是容器么?
- 我们使用的stack是属于哪个版本的STL?
- 我们使用的STL中stack是如何实现的?
- stack 提供迭代器来遍历stack空间么?
栈提供push 和 pop 等等接口,所有元素必须符合先进后出规则,所以栈不提供走访功能,也不提供迭代器(iterator)。 不像是set 或者map 提供迭代器iterator来遍历所有元素。
栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈的功能)。
所以STL中栈往往不被归类为容器,而被归类为container adapter(容器适配器)。
那么问题来了,STL 中栈是用什么容器实现的?
从下图中可以看出,栈的内部结构,栈的底层实现可以是vector,deque,list 都是可以的, 主要就是数组和链表的底层实现。
我们常用的SGI STL,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的底层结构。
我们常用的SGI STL,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的底层结构。
deque是一个双向队列,只要封住一段,只开通另一端就可以实现栈的逻辑了。
SGI STL中 队列底层实现缺省情况下一样使用deque实现的。
我们也可以指定vector为栈的底层实现,初始化语句如下:
std::stack<int, std::vector<int> > third; // 使用vector为底层容器的栈
1
刚刚讲过栈的特性,对应的队列的情况是一样的。
队列中先进先出的数据结构,同样不允许有遍历行为,不提供迭代器, SGI STL中队列一样是以deque为缺省情况下的底部结构。
也可以指定list 为起底层实现,初始化queue的语句如下:
std::queue<int, std::list<int>> third; // 定义以list为底层容器的队列
1
所以STL 队列也不被归类为容器,而被归类为container adapter( 容器适配器)。
我这里讲的都是C++ 语言中的情况, 使用其他语言的同学也要思考栈与队列的底层实现问题, 不要对数据结构的使用浅尝辄止,而要深挖其内部原理,才能夯实基础。
232.用栈实现队列
使用栈实现队列的下列操作:
push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。
示例:
MyQueue queue = new MyQueue();
queue.push(1);
queue.push(2);
queue.peek(); // 返回 1
queue.pop(); // 返回 1
queue.empty(); // 返回 false
说明:
-
你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
-
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
-
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。
此题目用两个栈模拟队列的操作,根据二者一个栈后进先出,另一个队列先进先出的特性,考虑两个栈来模拟队列的一系列的操作。主要定义两个栈,一个模拟输入的输入栈,另一个模拟输出的输出栈。
class MyQuene
{
stack<int> stIn;
stack<int> stOut;
MyQuene()
{
}
void push(int x)
{
stIn.push(x);
}
int pop()
{
if(stOut.empty())//如果输出栈为空则将输入栈中的元素全部输入到输出栈中。然后输出栈进行弹出
{
while(!stIn.empty())
{
stOut.push(stIn.top());
stIn.pop();
}
}
int result = stOut.top();
stOut.pop();
}
int peak()
{
int result=this->pop();
stOut.push(result);
}
bool isempty()
{
return stIn.empty()&&stOut.empty();
}
};
225. 用队列实现栈
使用队列实现栈的下列操作:
- push(x) – 元素 x 入栈
- pop() – 移除栈顶元素
- top() – 获取栈顶元素
- empty() – 返回栈是否为空
注意:
- 你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
- 你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
- 你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。
class MyStack {
public:
queue<int> que; // 使用一个队列来存储栈的元素
/** 初始化数据结构 */
MyStack() {
// 构造函数,用于初始化队列
}
/** 将元素 x 压入栈中 */
void push(int x) {
que.push(x); // 将元素 x 添加到队列的尾部
}
/** 弹出栈顶的元素并返回该元素 */
int pop() {
int size = que.size(); // 获取队列的大小
size--; // 减去1,因为要留下一个元素作为栈顶
// 将队列头部的元素(除了最后一个元素外)重新添加到队列尾部
while (size--) {
que.push(que.front()); // 将队列头部的元素添加到队列尾部
que.pop(); // 弹出队列头部的元素
}
int result = que.front(); // 此时队列的头部元素就是栈的顶部元素
que.pop(); // 弹出栈顶元素
return result;
}
/** 获取栈顶元素 */
int top() {
return que.back(); // 返回队列尾部的元素,即栈顶元素
}
/** 判断栈是否为空 */
bool empty() {
return que.empty(); // 判断队列是否为空,从而判断栈是否为空
}
};
这里只用了一个队列就模拟了