leetcode:232. 用栈实现队列

题目

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);
}

猜你喜欢

转载自blog.csdn.net/Zhou000815/article/details/112308690