一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情。
1、分析问题
开始今天的算法,用两个栈实现一个队列。要想解决这个问题,首先你要先弄清楚,什么是栈,什么是队列。
上一篇简单聊了一下栈:它是一种逻辑结构,在具体的编程语言中,需要具体实现,在上一篇文章中,我就用数组简单的实现了栈的操作,简而言之,栈最主要的特点就是先进后出。
那么什么是队列呢?队列同样是一种逻辑结构,不受语言的限制,在具体的语言中也需要具体的实现。它最主要的特点是先进先出。
而这道题的要求就是用两个先进后出的结构来实现一个先进先出的结构。在前端范围内,我首先想到的就是用数组进行模拟实现。下面让我们一起看一下如何实现。
2、解题思路
- 先看一下下面的图片
在这张图片中,简单的描绘出了解题的思路。
上方是先进先出的队列,下方两个是两个栈:stack1和stack2.对于队列,一般常用的属性或方法有三个:length,add(添加元素),delete(删除一个元素)。
- 入队的操作,可以用入栈模拟:进入stack1;
- 队列的长度length就可以用stack的长度来表示;
- 出队的操作呢,就是从stack1先通过pop方法将其中的元素全部转入stack2中,然后从stack2中在通过pop操作来模拟队列的出队操作;
- 为什么stack2中存在两个箭头呢?第一个实现表示pop操作,实现队列的出队的操作;而虚线箭头则表示在stack2进行pop操作后,需要将剩余元素再次入栈到stack1中。这样的目的是为了方便后续模拟入队的操作。如果不进行此操作,那么再次进行入队的操作的时候,虚线的G、F,你将它们直接入栈道stack2,如何再次模拟出队的操纵呢?下面查看具体的代码
解题
class MyQueue{
//定义两个私有变量栈
private stack1: number[] = [];
private stack2: number[]= [];
add(n: number){
this.stack1.push(n)
//这里如果按照push的返回值来计算的话,这里返回新数组stack1的长度
}
get length():number{
return this.stack1.length
}
delete():number | null{
let res;
const stack1 = this.stack1;
const stack2 = this.stack2;
//1、将stack1中的元素全部移动到stack2中
while(stack1.length){
const p = this.stack1.pop();
if(p != null){
stack2.push(p);
}
}
//2、模拟出队的操作
res = stack2.pop();
//3、将stack2中的元素再次移动到stack1中
while(stack2.length){
const s = stack2.pop();
if(s != null){
stack1.push(s)
}
}
return res || null
}
}
功能测试
const queue = new MyQueue();
queue.add(100)
queue.add(200)
queue.add(300)
queue.add(400)
console.log(queue.length)//4
console.log(queue.delete())//100
console.log(queue.length)//3
//符合预期
复杂度分析
空间复杂度是O(n)
这里的变量主要有stack1和stack2两个变量,其总长度或者说总的占据空间大小和队列的是一样的,所以为O(n);
时间复杂度O(n)
虽然这里使用了两个while循环,但是这两个循环并不是嵌套在一起的,所以时间复杂度是O(2n),但时间复杂度我们考虑的是数量级,所以最后时间复杂度定为O(n);
3、总结
这里主要考查了对栈和队列的特点,也考察了数组和队列的区别(类似于数组和栈的区别)。如果对于一道题没有思路的时候,我们是否可以考虑一下在纸上画一下,也许会豁然开朗呢。