特性
他与队列都是一种受限的线性结构,但是我们知道栈结构做大的特性是先进后出,任何的操作都是基于栈顶的操作,而队列,则是先进后出,压栈在栈顶操作,而出栈则是在栈低进行操作;
基于数组实现
class Queue {
arr: number[];
constructor() {
this.arr = [];
}
// 队列的操作;
// 进入队列(从队列的顶部进行操作)
enqueue(elemnet: number) {
this.arr.push(elemnet)
}
// 删除队列(从队列的底部进行操作)
dequeue() {
this.arr.shift()
}
}
let queue = new Queue();
就从代码复杂度来看,对列的代码与栈结构的复杂度差不多,没有什么太大的差别,但从性能来看,队列的性能远低于栈结构:如果只是单纯的增加的话,没有太大的区别,主要是出栈与出队列。出栈的话,他依旧是从栈顶进行操作,而整个栈结构并没有太大改变,无论里面的程序多呢复杂,运行起来都是相差无几的,但队列就不同了,出队列的话,则是从队列底部出栈,就单纯的论数组来看,每一次出栈,都会导致后面的元素重新排序,代码轻还行,但要是队列的复杂度、耦合度很高的话,使用对列就很麻烦了;
而这,也是队列性能低于栈结构的主要原因
优先级队列
用队列的方式进行排序
class PriorityQueue {
arr?: { element: string, level: number }[];
element?: string;
level?: number;
constructor() {
this.arr = []
this.element = ""
this.level = 0
}
QueueElement(element: string, level: number) {
this.element = element;
this.level = level;
return { element, level }
}
isEmpty() {
return this.arr.length === 0
}
// 实现插入的方法
enqueue(elment: string, level: number) {
let queueElement = this.QueueElement(elment, level);
// 情况一:判断队列是否为空;如果为空的话,直接放入
if (this.isEmpty()) {
this.arr.push(queueElement)
} else {
// 情况二:队列不为空,且用循环对比其等级,判断其插入的等级是否高于原数组的等级
// 如果高于其中的某一等,就将其插入其元素之前
// 并且这里做一个标记,用于检测循环中并未发现插入元素等级大于其所有元素,也就是说插入的元素的等级最低
let addElenemt = false
this.arr.forEach((item, index) => {
if (this.level > item.level) {
this.arr.splice(index, 0, queueElement);
addElenemt = true
return
}
})
// 如果元素的等级最低,则直接将其元素插值末尾
if (!addElenemt) {
this.arr.push(queueElement)
}
}
}
}
let priorityQueue = new PriorityQueue();
priorityQueue.enqueue("moannian1", 1)
priorityQueue.enqueue("moannian2", 2)
priorityQueue.enqueue("moannian3", 3)
// 此刻的arr:[{element:"moannian3",level:3},{element:"moannian2",level:2}{element:"moannian1",level:1}]
使用两个队列模拟栈结构
解题:我们想要做这一题,首先我们先要了解什么是栈结构:先进后出,后进先出
然后什么是队列:先进先出,后进后出
思路:双队列结构
这里,我们准备两个队列,一个是主队列,queue1,也就是我们主要存放队列的地方,一个是辅助队列queue2,是存放我们需要push的元素队列;首先我们先往辅助队列之中存储一个元素1;
此刻,我们检测到主队列为空了,将辅助队列赋值给主队列,然后将辅助队列清空,以便存入下一个元素
对的,我的思路就是这样的,将主队列中的元素为空时,而辅助队列不为空时,然后就将赋值队列中的值赋值给主队列,然后将赋值队列清空;
此时就有人疑惑了,只是恰巧主队列为空罢了,容易互相转换,那万一主队列中有值,且赋值队列中也有值呢;(且一直记住这一点栈的结构特性)
那好,我们此时加入一个元素2(记住,push的元素,永远存储到辅助队列中)
此时我们看见了,主队列并未空,且辅助队列中又有元素2,那么此时我们应该做的,就是想办法做的是:将主队列清空,已达到主队列与辅助队列的转换条件,且主队列中的元素并不能丢失;
我们要做的便是,将主队列元素pop出来,然后作为push的元素压入到辅助队列之中;
此时的主队列就为空了,就达到了主队列为空,辅助队列就与主队列相转换的原则了,此时我们转换一下看一看
我们暂时先不解释为什么这么做,我们先再按照以上的步骤在push一个元素3,且push的值依旧存储到辅助队列中
然后如法炮制,将主队列中的元素1和2依次取出,记住主队列也是队列,他的取出顺序也是依赖队列的原则,队列底部的元素先出来,也就是元素2先出来
然后取出元素1
主队列与辅助队列转换
此刻,我们举的例子完全结束,我们来回归一下,注意,回顾过程中,我们只关注主队列,不关注辅助队列,辅助队列,只是一个辅助我们的逻辑作用,不在最终结果讨论范围内;
我们最先push了元素1,然后push了元素2,最后push了元素3
假设:我们按照正常的队列逻辑来走,先进先出的原则:取出的顺序应该是:1、2、3;
但经过了我们一系列操作,结果正的如此吗?
就如我们上图来看:最先出来的一定是3,然后是2,最后是1;
此出队列顺序,正和我们如队列的顺序一致:最后压入3,最先出来3;最先压入的1,却是最后出来的, 正好符合我们的栈结构:先入后出,后入先出原则;
解题思路完毕,思路来自Leetcode;
提供本人所写代码;
class MyStack {
// 这里使用了两个数组来进行转换
queue1?: number[];
queue2?: number[];
constructor() {
this.queue1 = [];
this.queue2 = [];
}
push(x: number): void {
// 将push的元素压入辅助队列
this.queue2.push(x);
// 当主队列为空时转换,且清空赋值队列
if (this.empty()) {
this.queue1 = this.queue2;
this.queue2 = [];
return
} else {
// 如果主队列不为空时:将主队列中的值依次取出,且作为元素push到辅助队列中
this.push(this.pop())
}
}
// 出队列
pop(): number {
return this.queue1.shift();
}
// 这一步,始终指向栈顶元素
top(): number {
return this.queue1[0]
}
// 判断主队列是否为空
empty(): boolean {
return this.queue1.length === 0
}
}