队列(Queue)

队列和栈非常的类似,但是他们采用了不同的原则,栈采用的是后进先出,队列正好相反,采用的是先进先出的原则。队列的定义如下

队列是遵循FIFO(先进先出)原则的有序集合,新添加的元素保存在队列的尾部,要移除的元素保存在队列的顶部。在队列的这种数据结构里面,新增的元素都在尾部,要移除的元素都在顶部。

举一个生活中的例子,在我们平时去吃肯德基吃饭时肯定要排队,这条队伍就可以看做是一个队列,排在队伍前面的就是队列的顶部,队伍后面的就是队列的尾部,后来的人都要排在队伍的后面(队列的尾部),这就符合了队列先进先出的原则了(先排队的可以先点餐)。

代码实现

下面用代码来实现队列这个数据结构,同样都采用ES6的语法,我们先定义一个类Queue来表示队列,然后在这个类的基础上定义一下方法来模拟队列的行为。

class Queue {

  constructor() {
    // 定义一个数组来保存队列里面的元素
    this.items = []
  }

  // 在队列尾部添加一个或者多个元素
  enqueue(element) { }
    
  // 移除队列顶部的元素,并返回被移除的元素
  dequeue() { }
    
  // 清空队列
  remove() { }
    
  // 返回队列顶部的元素,不对队列本身做修改
  front() { }
    
  // 判断队列是否为空
  isEmpty() { }
    
  // 返回队列里面元素的个数
  length() { }
}

这样我们就定义好一个基类了,下面来分别实现队列的行为方法

enqueue

第一个要实现的就是enqueue方法,这个方法接收一个参数,然后把该参数插入队列的尾部,因为这里我们是用数组来存储队列的元素的,所以可以用数组的push方法来实现该操作,代码如下

 enqueue (element) {
   this.items.push(element)
 }

dequeue

下面接着实现dequeue方法,这个方法会从队列里面移除项,由于队列遵循的是先进先出的原则,所以我们要移除的元素就是队列顶部的元素,同样因为这里我们是用数组来存储队列的元素的,所以可以用数组的shift方法来实现该操作。代码如下

dequeue () {
    return this.items.shift()
}

remove

接着来实现remove方法,改方法会移除队列里面所有的项,同理我们把保存队列元素的数组置空就好了,代码如下

remove () {
    this.items = []
}

front

上面的方法都是对队列本身有修改的,接下来要实现的方法front做的是只读操作,front方法会返回队列顶部的元素但不对队列本身进行修改,代码实现如下

front () {
    return this.items[0]
}

isEmpty

isEmpty方法判断队列是否为空,也就是保存队列的数组的长度是否等于0,代码实现如下

isEmpty () {
    return this.items.length === 0
}

length

最后一个方法返回队列的长度,同理就是返回保存队列元素的数组的长度就好了,代码如下

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}`)
      })
  }
}

使用Queue类

要使用这个类我们得先实例化它

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请求,定时器等。


链接: https://fly63.com/course/28_1303