携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第 6 天,点击查看活动详情
前因
在上一篇文章中我们讲到了栈是一种先入后出的数据结构,并且也说了既然有先入后出的结构,肯定也有先入先出的数据结构,这一节我们就继续一起来学习一道关于对列的算法题 - 用栈实现队列,原题的地址在这里,题目的描述如下图所示:
在js\ts中我们也是用数组(array)来模拟队列,前面也说了队列是先入先出的数据结构,因此队列的相关API跟栈差不多,也是push、shift和length,下面我们就一起来简单实现一下队列吧!
队列
在前面我们已经说过队列是一种先进先出的数据结构,和栈一样,它本身也是不局限于任何语言的,下面我们一起来看一下用数组模拟队列的基本操作。
// 定于一个队列
const queue = [];
// 入队
queue.push(1);
queue.push(2);
queue.push(3);
const cur = queue.shift() // 出队
在前面旋转数组中我们讲到过,数组本身的shift操作是很慢的,但是这里我们先不用其它的数据结构来模拟队列,还是先用数组来实现,后续等学习新的数据结构链表时,再来看这个地方的问题。
下面我们一起来看一下这道题目的实现思路。
思路
我们这里是使用栈来实现队列,而栈的本质是先入后出,队列却是先入先出,那么我们该如何将栈的执行效果转换成队列呢?这里我们定义一个栈(stack1),入栈的时候是将数据通过push都压入栈底,例如第一次入栈是A,第二次是B,第三次是C,那么stack1里面的值就是[C, B, A],然后我们要可以通过出栈将stack1里面的输入再入栈到stack2里面,这时候stack2里面的数据就变成了[A, B, C],最后当我们需要出队的时候,stack2里面的A就从栈顶拿出来了,这样就实现了队列的先进先出原则。
一起来看一下具体的代码实现吧。
代码实现
/*
* 两个栈实现一个队列
*/
class MyQueue {
// 私有变量,数组模拟栈
private stack1: number[] = [];
private stack2: number[] = [];
// 队列的入队方法(添加方法)
add(num: number) {
this.stack1.push(num);
}
// 队列的出队方法(删除方法)
delete(): number | null {
// 定义一个临时变量
let res;
const stack1 = this.stack1;
const stack2 = this.stack2;
// 将 stack1 中所有元素都移动到 stack2 中
while (stack1.length) {
const cur = stack1.pop();
if (cur != null) {
stack2.push(cur);
}
}
// 拿到 stack2 中最底部的值(出队)
res = stack2.pop();
// 将 stack2 中所有的元素再返还给 stack1
while (stack2.length) {
const cur = stack2.pop();
if (cur != null) {
stack1.push(cur);
}
}
return res || null;
}
// 获取队列的长度
get length(): number {
return this.stack1.length;
}
}
上面我们完成了这个算法的基本实现,我们也可以在TypeScript官网的Playground中测试一下该方法是否能够正常运行,运行的结果如下图所示:
具体的执行效果可以狠戳这里
上面我们已经实现了这个算法,那么我们按照惯例继续对我们实现的算法来做一个性能的分析。
性能分析
在这个算法的实现中,add时间的复杂度是O(1),delete的时间复杂度是O(n);整体的空间复杂度是O(n)。因为两个栈中存储的数据其实是一样的,因此这里还是O(n)。
最后
我们总结一下这一节的大致内容。
队列是先进先出的数据结构,是非常重要的一种数据结构,在日常的算法中会经常用到队列这种数据结构,而栈跟队列的效果是相反的,因此我们如果要用两个栈来模拟队列,就需要先搞清楚它们之间的区别,这样再来实现这道算法题时相对就比较简单明了一些。
最后,如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,谢谢大家