今日的内容是“栈与队列理论基础”,栈是先进先出,队列是先进后出。力扣232和225题,主要考察对于栈和队列这两种数据结构的理解。在开始题目之前,从C和C++的角度,需要关注以下四个问题:
1. 在 C++ 中 stack 和 queue 是容器吗?(不是容器,是容器适配器)
2. 我们使用的 stack 是属于哪个版本的 STL?(存在多个版本,基础 HP STL,VC PJ STL,和 SGI STL,被 Linux 的 C++ 编译器 GCC 所采用)
3. 我们使用的 STL 中 stack 是如何实现的?(默认实现为双向队列 deque 为缺省情况下,限制 deque 的一部分接口,实现栈的数据结构)
4. 在 C++ 中,stack 提供迭代器来遍历 stack 空间吗?(不提供迭代器 iterator 遍历 stack 空间)
力扣232.用栈实现队列
请你使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty)。实现 MyQueue 类:
void push(int x):将元素 x 推到队列的末尾;
int pop():从队列的开头移除并返回元素;
int peek():返回队列开头的元素;
boolean empty():判定队列是否为空,如果为空返回 true,非空返回 true。
class MyQueue { // C++
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 result = stOut.top();
stOut.pop();
return result;
}
int peek() {
int res = this->pop();
stOut.push(res);
return res;
}
bool empty() {
return stIn.empty() && stOut.empty();
}
};
上面给出C++的实现,代码十分简单,主要是对于栈和队列的理解。在实现的 MyQueue 类中,我们定义了两个栈的成员变量,然后实现了若干成员函数,支持队列的操作。进队、取队首和判空都很简单,只有出队稍有复杂。因为队列要求先进先出,而栈只能先进后出,所以我们利用两个栈,输入栈和输出栈,当进栈时,向输入栈压入数据,当出栈时,将输入栈的数据压入输出栈,然后从输出栈栈顶出栈,一正一反,实现队列的先进先出。C++实现了很多栈的功能函数,对于栈和队列之间的关系表达的较为晦涩,这里我又实现了C语言版本:
typedef struct { // C
int stackInTop, stackOutTop;
int stackIn[100], stackOut[100];
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue* queue = (MyQueue*)malloc(sizeof(MyQueue));
queue->stackInTop = 0;
queue->stackOutTop = 0;
return queue;
}
void myQueuePush(MyQueue* obj, int x) {
if(obj->stackInTop != 100) {
obj->stackIn[obj->stackInTop++] = x;
}
}
int myQueuePop(MyQueue* obj) {
int stackInTop = obj->stackInTop;
int stackOutTop = obj->stackOutTop;
if(stackOutTop == 0) {
while(stackInTop != 0) {
obj->stackOut[stackOutTop++] = obj->stackIn[--stackInTop];
}
}
int top = obj->stackOut[--stackOutTop];
obj->stackInTop = stackInTop;
obj->stackOutTop = stackOutTop;
return top;
}
int myQueuePeek(MyQueue* obj) {
if(obj->stackOutTop != 0) {
return obj->stackOut[obj->stackOutTop - 1];
}
return obj->stackIn[0];
}
bool myQueueEmpty(MyQueue* obj) {
return obj->stackInTop == 0 && obj->stackOutTop == 0;
}
void myQueueFree(MyQueue* obj) {
obj->stackInTop = 0;
obj->stackOutTop = 0;
}
在C语言版本中,可以很明显的看到的栈的一些操作,进栈,出栈,判空,取栈顶,对于栈顶指针的移动和释放栈空间。相较于C++,C的实现更为具体一点。
力扣225.用队列实现栈
请你仅使用两个队列实现一个先进后出的栈,并支持普通栈的全部操作(push、top、pop、empty)。实现 MyStack 类:
void push(int x):将元素 x 压入栈顶;
int pop():移除并返回栈顶元素;
int top():返回栈顶元素;
boolean empty():如果栈是空的,返回 true,否则,返回 false。
class MyStack { // C++
public:
queue<int> que1;
queue<int> que2;
MyStack() {
}
void push(int x) {
que1.push(x);
}
int pop() {
int size = que1.size();
size--;
while (size--) {
que2.push(que1.front());
que1.pop();
}
int result = que1.front();
que1.pop();
que1 = que2;
while (!que2.empty()) {
que2.pop();
}
return result;
}
int top() {
return que1.back();
}
bool empty() {
return que1.empty();
}
};
基于C++的实现,主要的操作还是在于出栈,使用两个先进先出的队列,实现后进先出的栈。为了实现先进后出,在每次需要出栈的时候,我们使用一个备份队列,将除最后一个元素之外的所有元素,入队,然后将最后一个元素出队,再将备份队列中的元素出队并入队到原队列。备份队列的作用和 temp 变量一样,暂存元素。既然如此,我们通过一个队列,直接将除最后一个元素之外的所有元素,出队再重新入队,使最后一个元素变为队首,也同样能实现后进先出。
class MyStack { // C++
public:
queue<int> que;
MyStack() {
}
void push(int x) {
que.push(x);
}
int pop() {
int size = que.size();
size--;
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();
}
};
在C++中可以轻便地使用一个队列实现栈,在C中则相对费劲,因为本质上 queue 是基于动态数组实现的,数据结构的长度可以动态变化,添加或删除一个元素为 O(1) 的时间复杂度。而在C语言中,基于静态数组实现的队列,需要使用 front 指针标识当前队首的位置,当出队过多,会造成过多的无效内存空间;如果不使用 front 指针,则每当出队的时候都需要前移后面所有的元素,时间复杂度为 O(n),当然也可以通过动态分配内存的方式,实现动态数组的队列,但是较为复杂,且每次队列长度发生变化,都需要 realloc。下面仅给出基于C的两个队列实现栈的代码:
typedef struct { // C
int front1, rear1;
int front2, rear2;
int queue1[100], queue2[100];
} MyStack;
MyStack* myStackCreate() {
MyStack* stack = (MyStack*)malloc(sizeof(MyStack));
stack->front1 = 0;
stack->rear1 = 0;
stack->front2 = 0;
stack->rear2 = 0;
return stack;
}
void myStackPush(MyStack* obj, int x) {
obj->queue1[obj->rear1++] = x;
}
int myStackPop(MyStack* obj) {
int front1 = obj->front1, rear1 = obj->rear1;
int front2 = obj->front2, rear2 = obj->rear2;
while(front1 != rear1 - 1) {
obj->queue2[rear2++] = obj->queue1[front1++];
}
int result = obj->queue1[front1];
front1 = 0;
rear1 = 0;
while(front2 != rear2) {
obj->queue1[rear1++] = obj->queue2[front2++];
}
front2 = 0;
rear2 = 0;
obj->front1 = front1;
obj->rear1 = rear1;
obj->front2 = front2;
obj->rear2 = rear2;
return result;
}
int myStackTop(MyStack* obj) {
return obj->queue1[obj->rear1 - 1];
}
bool myStackEmpty(MyStack* obj) {
return obj->front1 == obj->rear1;
}
void myStackFree(MyStack* obj) {
obj->front1 = 0;
obj->rear1 = 0;
}