栈和队列
栈是一种FILO(first in last out)先进后出的数据结构,即先进入栈的数据会保存在靠近栈底的位置,而后进入的数据会离栈顶更近,而栈顶是唯一的数据出口,先进入的数据后出,后进入的数据先出,一个形象的表示,当你去买书时,在一摞相同的书中,最方便的方式会选择购买最上层的书,这摞书就是一个栈,先进后出。
栈的定义:
栈是一种特殊的线性表,其插入(也称为入栈或压栈)和删除(也称为出栈或弹栈)操作都在表的同一端进行。这一端称为栈顶(top),另一端称为栈底(bottom)。
由于栈是一种特殊的线性表,所以从相应的线性表类(数组和链表)派生出栈类是非常正常的事情。所以可以通过数组和链表实现的栈,在C++的STL库中,栈的底层实现可以是vector、deque和list等,而由于是运用其他容器进行实现,STL中的栈往往不被归类为容器,而被归类为container adapter(容器适配器)。
队列是一种FIFO(first in first out)先进先出的数据结构,一个简单的举例,在银行窗口前的排队,先排队的人先服务,后到的人需要等待。
队列的定义:队列是一个线性表,其插入和删除操作分别在表的不同端进行。插入元素的那一段称为队尾(back或rear),删除元素的那一端称为队首(front)。
同样队列也可以从相应的线性表类中派生出,所以,它也被归类为容器适配器。
用栈实现队列
题目要求仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty),这些操作分别表示,将元素x放入队尾、从队头取出元素、返回队头的元素、判断队列是否为空。
首先要求是通过两个栈实现先入先出队列,栈是后入先出的数据结构,想要将后入先出的栈组成一个先入先出的队列,一个简单的思路是将元素全部入栈到栈1,在需要弹出数据时,将所有数据的出栈到栈2中,再从栈2出弹出数据,此时的数据就是最先进入栈1的数据。即实现了数据的先入先出。
class MyQueue {
private:
stack<int>stackin,stackout;//创建2个栈stackin和stackout
public:
MyQueue() {
}
void push(int x) {//若stackout非空,将所有的元素搬回出栈,入栈到stackin,然后push
while(!stackout.empty()){
stackin.push(stackout.top());
stackout.pop();
}
stackin.push(x);
}
int pop() {//若stackin非空,将stackin中所有元素入栈到stackout中,stackout出栈
while(!stackin.empty()){
stackout.push(stackin.top());
stackin.pop();
}
int x = stackout.top();
stackout.pop();
return x;
}
int peek() {//同pop,不过返回的是stackout.top().
while(!stackin.empty()){
stackout.push(stackin.top());
stackin.pop();
}
return stackout.top();
}
bool empty() {//当stackin和stackout皆空,则为空,否则非空
return(stackin.empty() and stackout.empty());
}
};
这里有一个问题在于每次pop和push都需要频繁的入栈和出栈,实际上,当stackout非空时,push操作只需要stackin.push(x)就行,而当要pop时,判断stackout.empty(),若为空,将stackin中所有元素入栈到stackout中,即能省去一些没必要的操作。时间复杂度push、pop和peek都为O(n),empty的时间复杂度为O(1),空间复杂度均为O(1)。
用队列实现栈
题目要求用两个队列来模拟栈,此时由于队列这个数据结构的先入先出的特点,并不能用前面用栈实现队列那样来回倒的方式实现栈,考虑栈的先进后出特性,当第一个队列出到只剩最后一个元素时,即为栈中最先出的元素,可以考虑创建两个队列,一个队列用于出最后一个元素,另外一个队列用于存放前面的所有元素。通过队列模拟栈的代码如下:
class MyStack {
private:
queue<int> stack;//创建模拟栈的队列
queue<int>backups;//创建备份队列
public:
MyStack() {
}
void push(int x) {
stack.push(x);
}
int pop() {
while(stack.size()!=1){
backups.push(stack.front());
stack.pop();
}
int x = stack.front();
stack.pop();
while(backups.size()){
stack.push(backups.front());
backups.pop();
}
return x;
}
int top() {
while(stack.size()!=1){
backups.push(stack.front());
stack.pop();
}
int temp = stack.front();
backups.push(stack.front());//注意这里要将所有的元素全放进backups再重新恢复stack队列
stack.pop();
while(backups.size()!=0){
stack.push(backups.front());
backups.pop();
}
return temp;
}
bool empty() {
return(stack.empty());//当栈队列为空,则为空,因为每次都会复原
}
};
这里的一个点在于top操作,需要记住将所有stack队列中的元素都放进backup队列中,再考虑将backup中元素重新放回stack中。即每次复原stack队列前要确保stack队列为空,否则会导致乱序。push的时间复杂度为O(1),pop()、top()的时间复杂度为O(n),空间复杂度为O(n)。