(给算法爱好者加星标,修炼编程内功)
作者:vitasoy25 / 在所不辞 (本文来自作者投稿)
问题描述
如何用数组实现固定长度的栈和队列?【基础】
如何只用队列实现一个栈?【有一定技巧】
如何只用栈实现一个队列?【有一定技巧】
如何实现一个最小栈,即一个具备返回最小值函数的栈?【有一定难度和技巧性】
使用数组实现固定长度的栈
对于大多数人而言,这题就是送分题,当然,在面试场景下,如何确保又快又准地完成这道题目,就需要考验我们的coding
能力了,对于自认为基础较好的同学,建议花一两分钟思考下push
和pop
函数的设计,然后再往下看进行验证;对于自认基础一般的同学,建议花5-10 分钟
写一下这个代码,写完再进行验证;对于没有学过的同学,建议看看思路即可。
废话少说,我们都知道栈的特性:先进后出,后进先出(FILO),因此,关键考虑点就是push
函数和pop
函数如何设计。
首先我们构建一个栈类:
class Stack {
push
函数,首当其冲的就是边界考虑,如果栈已经满了,我们不能继续加到数组中,否则会溢出;至于push
进去,我们使用size++
非常方便和简洁,因为size
一开始是0
,而第一个位置的下标也是0
,当我们推入一个数字后,size
又应该增加一。
void push(int num) {
pop
函数,依然要考虑边界,如果栈已经是空的了,那么就不能继续推出;至于被推出的数据我们并不需要做特殊的处理,实际上我们只需要标记好栈的当前长度即可。
int pop() {
另外,这里我们模仿了java
中的栈的弹出写法,不像cpp
中为无返回值,我们会返回被推出的数的值。
使用数组实现固定长度的队列
所谓队列,即先进先出,后进后出(FIFO)的结构。有了前面写栈的经验,这里就不多说了,重点依然是边界,当然队列我们需要充分利用数组的空间,因此需要两个变量,分别记录开始和结尾。
构造队列类:
class Queue {
push
函数:
void push(int num) {
要注意,当end
到达数组的末尾,但当前长度 < 数组大小
时,说明数组前部分还有空位(即begin > 0
)。
如果size
和arr.size() - 1
相等,说明当前队列已经到达末尾了,end
需要回到开头;否则,end
向后移即可。
pop
函数:
int pop() {
关于这句begin = (begin == arr.size() - 1) ? 0 : begin + 1;
,如果begin
到达了队列的末尾,此时pop
弹出,那么begin
需要回到数组开头,否则,begin
自增即可。
如何只用队列实现一个栈
只用队列实现栈,没有接触过这道题的同学,咋一看会比较懵逼,但实际上往往是忽略了队列的个数。事实上我们可以使用两个队列来实现。
如下图所示,我们使用两个队列queue
和help
来实现这个栈:
首先我们先在queue
˙中放入三个数,注意这里使用的是队列,此时我们想要弹出3
,而队列只能取最前面的数,因此我们要另辟蹊径:
queue.size() > 1
,将queue
的队首弹出并压入help
中;queue.size() > 1
,将queue
的队首弹出并压入help
中;queue.size() == 1
,暂存当前队首(栈顶元素),这是我们待会要返回出去的数;
也是最关键的一步,我们将
queue
和help
交换:返回前面暂存的栈顶元素即可。
至此,我们相当于完成了pop
函数,而push
函数,实际上判断一下边界(如果有的话),存入queue
中即可。
代码实现:
class Stack{
如何只用栈实现一个队列
有了前面用队列实现栈的经验,接下来考虑用栈实现队列也会更加简单。同样的,我们使用两个栈来实现队列。
队列是先进先出,而栈是后进先出,那么我们怎么用栈实现呢?脑子灵光的同学们可能已经想到了:把一个栈倒入另一个栈里:
关键部分来了,我们只需每次倒栈的时候将pushStack
中的数据全部倒入popStack
,且popStack
为空时才进行倒栈操作即可。(大家可以思考一下为什么?)
倒栈操作实现:
void pushToPop() {
其他部分代码:
class Queue{
如何实现一个最小栈,即一个具备返回最小值函数的栈?
这一题就比较有意思了,我们如何实现一个栈,能随时的获取这个栈里面的最小值呢?可能有朋友会不假思索的说这很简单,用一个变量来存最小值就好了。嘿,这就忘了一点了,栈是会变化的,假如当前栈顶是最小值,你弹出之后,去哪里找次小值呢?
大部分同学在此处就会懵逼了,那应该怎么做呢?
其实很简单,我们再拿一个栈来存储最小值即可:即我们有一个dataStack
和一个minStack
,前者存储数据,后者存储最小值;那么最小值该怎么存呢?
和数据栈并行的存,也就是说最小栈的长度和数据栈的长度一致,每个数据代表同等长度下的栈的最小值。
画个图你们就明白了:
当我们一开始压入
2
时,因为此时minStack
为空,所以直接压入即可;当我们压入
3
的时候,此时3 > minStack.top()
即3 > 2
,因此,我们压入当前的最小值2
;当我们压入
1
的时候,此时1 < minStack.top()
即1 < 2
,因此,我们压入新的最小值1
。弹栈操作则更加简单,
minStack
只需跟随dataStack
进行弹栈即可。
代码实现如下:
class MinStack {
【本文作者】
在所不辞:双非一本,科班出身;大二即进入腾讯实习,目前专注写面试相关的算法、计算机网络、操作系统等基础知识;助力研发面试,共同成长!
推荐阅读 点击标题可跳转算法一看就懂之「 堆栈 」
图解堆算法、链表、栈与队列
觉得本文有帮助?请分享给更多人
关注「算法爱好者」加星标,修炼编程内功
好文章,我在看❤️