队列和栈非常的类似,但是他们采用了不同的原则,栈采用的是后进先出,队列正好相反,采用的是先进先出的原则。队列的定义如下
队列是遵循FIFO(先进先出)原则的有序集合,新添加的元素保存在队列的尾部,要移除的元素保存在队列的顶部。在队列的这种数据结构里面,新增的元素都在尾部,要移除的元素都在顶部。
举一个生活中的例子,在我们平时去吃肯德基吃饭时肯定要排队,这条队伍就可以看做是一个队列,排在队伍前面的就是队列的顶部,队伍后面的就是队列的尾部,后来的人都要排在队伍的后面(队列的尾部),这就符合了队列先进先出的原则了(先排队的可以先点餐)。
下面用代码来实现队列这个数据结构,同样都采用ES6的语法,我们先定义一个类Queue来表示队列,然后在这个类的基础上定义一下方法来模拟队列的行为。
class Queue {
constructor() {
// 定义一个数组来保存队列里面的元素
this.items = []
}
// 在队列尾部添加一个或者多个元素
enqueue(element) { }
// 移除队列顶部的元素,并返回被移除的元素
dequeue() { }
// 清空队列
remove() { }
// 返回队列顶部的元素,不对队列本身做修改
front() { }
// 判断队列是否为空
isEmpty() { }
// 返回队列里面元素的个数
length() { }
}
这样我们就定义好一个基类了,下面来分别实现队列的行为方法
第一个要实现的就是enqueue方法,这个方法接收一个参数,然后把该参数插入队列的尾部,因为这里我们是用数组来存储队列的元素的,所以可以用数组的push方法来实现该操作,代码如下
enqueue (element) {
this.items.push(element)
}
下面接着实现dequeue方法,这个方法会从队列里面移除项,由于队列遵循的是先进先出的原则,所以我们要移除的元素就是队列顶部的元素,同样因为这里我们是用数组来存储队列的元素的,所以可以用数组的shift方法来实现该操作。代码如下
dequeue () {
return this.items.shift()
}
接着来实现remove方法,改方法会移除队列里面所有的项,同理我们把保存队列元素的数组置空就好了,代码如下
remove () {
this.items = []
}
上面的方法都是对队列本身有修改的,接下来要实现的方法front做的是只读操作,front方法会返回队列顶部的元素但不对队列本身进行修改,代码实现如下
front () {
return this.items[0]
}
isEmpty方法判断队列是否为空,也就是保存队列的数组的长度是否等于0,代码实现如下
isEmpty () {
return this.items.length === 0
}
最后一个方法返回队列的长度,同理就是返回保存队列元素的数组的长度就好了,代码如下
length () {
return this.item.length
}
这里和栈一样添加一个辅助方法print来打印栈里面的元素,方便我们观察调试,这个方法和队列的行为无关,只是一个辅助方法
print(){
this.items.forEach((item, index) => {
console.log(`${index+1}:${item}`)
})
}
这样队列的方法就全部写好了,最后完整Queue类的代码如下
class Queue {
constructor() {
// 定义一个数组来保存队列里面的元素
this.items = []
}
// 在队列尾部添加一个或者多个元素
enqueue (element) {
this.items.push(element)
}
// 移除队列顶部的元素,并返回被移除的元素
dequeue() {
return this.items.shift()
}
// 清空队列
remove() {
this.items = []
}
// 返回队列顶部的元素,不对队列本身做修改
front() {
return this.items[0]
}
// 判断队列是否为空
isEmpty() {
return this.items.length === 0
}
// 返回队列里面元素的个数
length() {
return this.item.length
}
print(){
this.items.forEach((item, index) => {
console.log(`${index+1}:${item}`)
})
}
}
要使用这个类我们得先实例化它
const queue = new Queue()
queue.isEmpty() // true
queue.push('我是队列的第一个元素')
queue.push('我是队列的第二个元素')
queue.print() // 1: 我是队列的第一个元素 2:我是队列的第二个元素
queue.dequeue() // 我是队列的第一个元素
queue.front() // 我是队列的第二个元素
queue.length() // 1
queue.isEmpty() // false
queue.remove() // 这时队列里面已经没有元素了
queue.isEmpty() // true
队列这种数据结构运用的是非常的广泛的,就比如javaScript的运行机制,我们都知道javaScript是单线程的,是不能同时执行多个任务,但是单线程就意味着所有任务需要排队。但是在javaScript里面,很多时候阻止线程运行的很慢的是网络IO之类,这时候CPU是空闲的,这样就会造成资源的浪费。所以javaScript在主线程之外实现了一个任务队列,像IO之类比较慢的操作暂时都会挂在任务队列上,这样不会影响到主线程上任务的执行,等到IO的响应回来后再回到主线程上来执行挂起的任务。例子就是我们的Ajax请求,定时器等。