Bootstrap

经典面试题之-用两个栈实现一个队列(超详细!简单易懂!)

本文章适用于了解过栈和队列结构的读者阅读,本文章代码使用c++实现!

目录

1.栈和队列的特点

1.1: 栈的特点

1.2: 队列的特点

2.如何使用两个栈实现一个队列?

2.1分析结构

2.2注意事项!

3.分析结束,开始写代码

3.1整体代码框架

3.2函数PushToPop

3.3函数empty

3.4函数push

3.5函数front

3.6函数Pop

4.简单测试

5.整体代码

6.leetcode题目和题目链接

7.总结


1.栈和队列的特点

栈和队列都是比较常见的数据结构

1.1: 栈的特点

栈的特点就是后插入的数据先出来,所谓后进先出。

一个经典的例子就是手枪中的子弹,最后压入的子弹最先打出来

1.2: 队列的特点

先插入的数据先出来,先进先出

一个经典的例子就是排队

2.如何使用两个栈实现一个队列?

假如面试官现在给你提供了两个完备的栈,你可以使用栈中的各种操作,如pop,push,top等。

最后能够实现队列中的pop,push,front等操作

本文使用c++ stl中的stack作为面试官提供的栈。

2.1分析结构

要实现队列操作,假如插入数据 1 2 3 4 5 ,那么最后输出 1 2 3 4 5

那么我们可以在第一个栈中插入数据,然后再将第一个栈中的数据弹出到第二个栈中

第二个栈在弹出数据给使用者即可完成

我们在第一个栈中插入数据

然后再将数据弹出并且插入到第二个栈中

由于栈的后进先出,所以栈2中的数据如下图所示

然后我们只要将栈2中的数据依次弹出即可得到队列输出的数据1 2 3 4 5

这样就能够模拟队列输入 1 2 3 4 5并且输出 1 2 3 4 5的操作

2.2注意事项!

假设我们称第一个栈为push栈,第二个栈为pop栈

每当我们将push栈中的数据弹出并且插入到pop栈的时候要将push栈中的所有数据到弹出并且插入到pop栈中,否则就会导致输出错误。

同上我们画图进行分析假设我们先插入了 1 2 3三个数据,最后因该输出 1 2 3

假设我们只吧 2 和 3 插入到第二个栈中,然后再弹出数据就会出错

输出的是 2 3 ,1没有输出

同理我们每次从pop栈弹出数据的时候要注意要将所有数据都输出之后,才能再次从push栈中插入新的数据

假设我们插入 1 2 3 ,然后输出 1 2   再插入4 5  最后输出 3 4 5

正确的输出因该是 1 2 3 4 5

如下流程图

然后我们输出 1 和 2

现在我们插入4 和 5并且在pop栈有数据的情况下

将push栈中的数据插入到pop栈中

这样的话,最后就会输出 4 5 3        出现错误

这是因为我们没有将3输出就插入了4和5,因该先弹出3就不会出现错误。

3.分析结束,开始写代码

3.1整体代码框架

#include<iostream>
#include<stack>
#include<assert.h>
using namespace std;

class MyQueue
{
public:
	MyQueue()
	{
		stack<int> s_push;
		stack<int> s_pop;
	}

	int pop()
	{}

	void push()
	{}

	int front()
	{}

	bool empty()
	{}

private:
	void PushToPop()
	{}
	stack<int> s_push;
	stack<int> s_pop;
};

int main()
{

}

3.2函数PushToPop

由于我们在处理过程中,需要多次将push栈中的数据导入到pop栈中

所以在私有中写一个函数来完成这个功能,以提高代码的复用性和稳健性。

(此函数只能在类内部使用)

这个函数的功能为当pop栈为空的时候,将push栈中的所有数据插入到pop栈中

如果pop栈不为空,则不导入数据,防止出差

代码如下

void PushToPop()
	{
		//当pop栈为空时才导入数据
		if (s_pop.empty())
		{
			//将push所有数据导入到pop栈中
			while (!s_push.empty())
			{
				s_pop.push(s_push.top());//pop栈插入push栈的栈顶元素
				s_push.pop();//push栈删除栈顶元素
			}
		}
	}

3.3函数empty

此函数的功能是判断队列是否为空

如果pop栈和push栈都为空则说明,整个队列中没有元素

代码如下

bool empty()
	{
		return (s_pop.empty() && s_push.empty());
	}

3.4函数push

直接在push栈中插入数据并且使用PushToPop函数判断能不能导数据即可

代码如下

void push(int x)
	{
		s_push.push(x);
		PushToPop();
	}

3.5函数front

这个函数的功能是返回队列最前面的元素(即pop栈的栈顶元素)

直接判断返回即可

代码如下

	int front()
	{
		assert(!empty());//断言队列不为空,这里直接调用empty函数
		PushToPop();//导数据
		return s_pop.top();
	}

3.6函数Pop

每次删除前使用PushToPop判断pop栈中是否为空,然后再删除数据即可

由于PushToPop函数只有pop栈为空才会导数据,所以不会导致错误地导数据

代码如下

int pop()
	{
		assert(!empty());//断言不为空
		int t = front();//部分面试官在pop函数中要求返回pop的元素(就是队首的元素),这里记录一下
		s_pop.pop();//删除pop
		return t;
	}

4.简单测试

测试代码如下

void test_Myqueue()
{
	MyQueue q;
	q.push(1);
	q.push(2);
	q.push(3);
	q.pop();
	q.push(4);
	q.push(5);
	q.push(6);
	q.push(7);
	q.pop();
	//最后输出的因该是 3 4 5 6 7
	while (!q.empty())
	{
		cout << q.front() << " ";
		q.pop();
	}
}

int main()
{
	test_Myqueue();
	return 0;
}

运行结果如下图

5.整体代码

#include<iostream>
#include<stack>
#include<assert.h>
using namespace std;

class MyQueue
{
public:
	MyQueue()
	{
		stack<int> s_push;
		stack<int> s_pop;
	}

	int pop()
	{
		assert(!empty());//断言不为空
		int t = front();//部分面试官在pop函数中要求返回pop的元素(就是队首的元素),这里记录一下
		s_pop.pop();//删除pop
		return t;
	}

	void push(int x)
	{
		s_push.push(x);
		PushToPop();
	}

	int front()
	{
		assert(!empty());//断言队列不为空,这里直接调用empty函数
		PushToPop();//导数据
		return s_pop.top();
	}

	bool empty()
	{
		return (s_pop.empty() && s_push.empty());
	}

private:
	void PushToPop()
	{
		//当pop栈为空时才导入数据
		if (s_pop.empty())
		{
			//将push所有数据导入到pop栈中
			while (!s_push.empty())
			{
				s_pop.push(s_push.top());//pop栈插入push栈的栈顶元素
				s_push.pop();//push栈删除栈顶元素
			}
		}
	}
	stack<int> s_push;
	stack<int> s_pop;
};


void test_Myqueue()
{
	MyQueue q;
	q.push(1);
	q.push(2);
	q.push(3);
	q.pop();
	q.push(4);
	q.push(5);
	q.push(6);
	q.push(7);
	q.pop();
	//最后输出的因该是 3 4 5 6 7
	while (!q.empty())
	{
		cout << q.front() << " ";
		q.pop();
	}
}

int main()
{
	test_Myqueue();
	return 0;
}

6.leetcode题目和题目链接

        在力扣中有这道题,我们将代码输入到力扣中看一下是否能够通过力扣的测试

题目链接        232. 用栈实现队列 - 力扣(LeetCode)

由于在力扣中返回队首元素的函数名为peek,将front改为peek

点击提交后就能通过测试,并且用时超过100%。

7.总结

本体考察我们对栈和队列的了解程度,如果栈和队列都学的比较扎实此题因该不难!

只要注意两个注意事项即可!

下篇文章我会详细介绍如何使用两个队列来实现一个栈!

对你有用的话点个赞收藏关注一下吧~

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;