题目
232.用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列的支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false
说明:
你只能使用标准的栈操作 —— 也就是只有 push to top, peek / pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
进阶:
你能否实现每个操作均摊时间复杂度为 O(1) 的队列?换句话说,执行 n 个操作的总时间复杂度为 O(n) ,即使其中一个操作可能花费较长时间。
示例:
输入:
[“MyQueue”, “push”, “push”, “peek”, “pop”, “empty”]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]
解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/implement-queue-using-stacks
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路以及图解
思路过程
要通过栈实现队列,我们先了解他们的区别和共同点。
- 栈是一端(栈顶)进出,达到的后进先出,
- 队列是队尾进,队头出,达到的先进先出;
都是尾入,所以插入数据不存在问题。问题在于出数据,栈出的是后进的数据(出栈顶),此时出队头,队头数据在栈底,正常出栈拿不到,设想如果连着出数据的话,相当于从栈底到栈顶出数据,刚好和正常的出栈相反。所以我们逆置栈,再出栈就解决了出队头的问题。
但是问题又来了,如果栈内的数据没出完,又入栈了新的数据,此时就破坏了原来的顺序。总不能每次入栈的时候,再创建一个新的栈吧。
不如创建两个栈,一个用来存进来的数据(qpush),一个用来出数据(qpop),存的时候,正常的入栈,出数据的时候,我们把qpush的数据出栈再入栈到qpop完成逆置,再出数据就从qpoo里出,如果qpop出完了,再从qpush里面按上述导入逆置,这样既能保证原有数据顺序不会变化,又可以避免每次入栈都开辟新栈的问题。
最终思路:
入数据:
获取队头数据:
再入数据、出数据、获取数据:
当qpop为空时,再进行数据导入逆置:
上述演示操作顺序:
完美解决这道,下面实现代码。
源代码
// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
STDataType* _a;
int _top; // 栈顶 数组下标,使用数组最后一个元素时,应该减一。 //也可以用对应的指示法。
int _capacity; // 容量
}Stack;
// 初始化栈
Stack* StackInit(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool StackEmpty(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);
// 初始化栈
Stack *StackInit(Stack* ps)
{
ps = (Stack*)malloc(sizeof(Stack));
if (ps)
{
ps->_a = (STDataType*)malloc(sizeof(STDataType)* 4);
ps->_top = 0; //这里如果用0,代表第一个元素的位置,那就是数组下标法,先进行数据填入,再将数组下标++。显示栈顶数据,需要将[ps->_top -1]。
ps->_capacity = 4;
}
return ps;
}
// 入栈
void StackPush(Stack* ps, STDataType data)
{
if (ps->_top < ps->_capacity)
{
ps->_a[ps->_top] = data;//注意!数组下标!这里要先将数据放进去,再给top++,如果先++,就越界访问了。
ps->_top++;
}
else
{
STDataType*newtop = (STDataType*)realloc(ps->_a, sizeof(STDataType)*ps->_capacity * 2);
{
if (newtop)
{
ps->_a = newtop;
ps->_capacity *= 2;
ps->_a[ps->_top] = data;//注意!数组下标!这里要先将数据放进去,再给top++,如果先++,就越界访问了。
ps->_top++;
}
}
}
}
// 出栈
void StackPop(Stack* ps)
{
if (ps->_top == 0)
{
return;
}
else
{
ps->_top--;
}
}
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
return ps->_a[ps->_top - 1];
}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
int size = ps->_top;
return size;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool StackEmpty(Stack* ps)
{
if (ps->_top)
{
return false;
}
else
{
return true;
}
}
// 销毁栈
void StackDestroy(Stack* ps)
{
int rel = StackEmpty(ps);
free(ps->_a);
ps->_a = NULL;
free(ps);
ps = NULL;
}
typedef struct {
//这里选择给两个栈结构体指针,因为上面的写的栈的初始化接口,是在栈初始化接口里面创建栈(malloc出一个栈),返回栈的地址,我们在 myQueueCreate()接口把返回值赋给这俩指针。
//还有一种初始化方法:我们在外部创建好栈,传栈的地址在初始化接口里。不需要返回值。
Stack* qPush;
Stack* qPop;
} MyQueue;
/** Initialize your data structure here. */
int myQueuePeek(MyQueue* obj);
MyQueue* myQueueCreate() {
MyQueue *q = (MyQueue*)malloc(sizeof(MyQueue)); //这个队列必须要malloc,如果直接创建队列,就是一个局部数据,这个myQueueCreate()接口一结束,队列就释放返回给系统了。无法使用
q->qPush = StackInit(q->qPush);
q->qPop = StackInit(q->qPop);
return q;
}
/** Push element x to the back of queue. */
void myQueuePush(MyQueue* obj, int x) {
StackPush(obj->qPush, x);
}
/** Removes the element from in front of queue and returns that element. */
int myQueuePop(MyQueue* obj) {
int peek = myQueuePeek(obj); //代码的复用!比较实用,但是要注意使用场景,
StackPop(obj->qPop); // 你可能会问:为什么不直接从qpop里面删除栈顶?干嘛还获取一下。
return peek; //第一:获取数据并且保存,是为了返回这个被删掉的数据,注意题目要求!
//第二:如果qpush里面不为空,qpop里面为空呢?题目上说是不会对空队列操作的,但是此时是空队列吗?所以才获取一下,保证qpop不为空。
}
/** Get the front element. */
int myQueuePeek(MyQueue* obj) {
if (!StackEmpty(obj->qPop))
{
return StackTop(obj->qPop);
}
while (!StackEmpty(obj->qPush))
{
StackPush(obj->qPop, StackTop(obj->qPush));
StackPop(obj->qPush);
}
return StackTop(obj->qPop);
}
/** Returns whether the queue is empty. */
bool myQueueEmpty(MyQueue* obj) {
if (StackEmpty(obj->qPush) && StackEmpty(obj->qPop)) //都为空才是空队列哦!
{
return true;
}
else
{
return false;
}
}
void myQueueFree(MyQueue* obj) {
StackDestroy(obj->qPush);
StackDestroy(obj->qPop);
free(obj);
}