从零学习数据结构(3)- 认识队列和队列的基础练习

163 阅读4分钟

「这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

前言

之前的文章,认识栈结构,并用数组的相关方法,实现了一个栈的类结构,并提供了增、删、改、查的相关方法。接下来就来认识一下队列结构!

认识队列

什么是队列(Queue)

  • 也是一种受限的线性表,先进先出

  • 受限之处在于它只允许在表的 前端 进行删除操作

  • 只能在表的 后端 进行插入操作

生活中的队列结构

  • 排队

  • WC

开发中的队列结构

1、打印队列:

  • 有五份文档需要打印,这些文档会按照顺序放入打印队列中

  • 打印机会依次从队列中取出文档,先放入的先取出,然后打印文档

  • 依次打印五分文档

2、线程队列

  • 在开发中,为了让任务可以并行处理,通常会开启多个线程

  • 但是,我们不能让大量线程同时运行处理任务(导致占用过多资源)

  • 这时,在有需要开启线程处理任务的情况下,我们需要开启线程队列

  • 线程队列会按顺序来启动线程,并处理对应的任务

队列的实现

这里也使用数组,来实现一个队列的类

队列的常见操作

  • enqueue:向队列尾部添加一个(或多个)新的项

  • dequeue:移除队列的第一项,并返回这一被删除的项

  • front:返回队列中的第一个元素,并且不修改队列

  • isEmpty:判断这个队列中是否为空

  • size:返回队列里的元素个数

  • toString:将队列的内容以字符串的形式返回

代码

class Queue {
  items = []

  enqueue(element) {
    this.items.push(element)
  }
  dequeue() {
    return this.items.shift()
  }
  front() {
    return this.items[0]
  }
  isEmpty() {
    return this.items.length === 0
  }
  size() {
    return this.items.length
  }
  toString() {
    return this.items.join('')
  }
}

测试代码

const queue = new Queue()
queue.items = ['a', 'b', 'c', 'd']
console.log(queue.dequeue())  // 'a'
console.log(queue.front())    // 'b'
console.log(queue.enqueue(1))  // undefined
console.log(queue.size())     // 5 
console.log(queue.isEmpty())  // false
console.log(queue.toString()) // 'bcd12'

队列的常见面试题

击鼓传花

规则:

1、班级中玩一个游戏,所有学生围成一圈,从某位学生开始向旁边同学传递一朵花

2、裁判在击鼓,鼓声停下时,花在谁手上就要表演节目

3、将表演节目修改为直接淘汰

4、最后剩下的人获得胜利,请问这个胜利者是哪个位置的人?

封装一个基于队列的函数:

  • 参数:所有参与者的姓名、间隔的数字

  • 结果:胜利者的姓名

const Queue = require('./queue-demo')

function passFlowerGame(nameList, number) {
  const queue = new Queue()

  for (let i = 0; i < nameList.length; i++) {
    queue.enqueue(nameList[i])
  }

  while (queue.size() > 1) {
    for(let i = 0; i < number - 1; i++) {
      queue.enqueue(queue.dequeue())
    }
    queue.dequeue()
  }
  console.log(queue.toString())
  return queue.front()
}

console.log(passFlowerGame([11,22,33,44,55], 2))   // 33

关于优先级队列

特点

  • 普通队列插入一个元素时,该元素会被放入到后端,并且需要等之前的元素处理完,才会处理这个数据

  • 但是优先级队列,在插入一个元素时会考虑该数据的优先级,与其他数据的优先级进行比较

  • 比较完成后,得出这个元素应该在队列中的正确位置

优先级队列主要考虑的问题

  • 每个元素不再只是一个数据,而且包含了该数据的优先级

  • 根据数据的优先级来放入正确的位置

现实例子

1、登机顺序:

  • 头等舱、商务舱乘客优先级大于经济舱;

  • 在部分国家,老人、孕妇、儿童登机,也享有更高的优先级

2、医院急诊科排队

  • 医生会优先处理病情更严重的患者

3、计算机中,也是通过优先级队列来重新排序队列中任务的顺序

  • 根据每个线程的重要性不同,通过优先级的大小,来决定该线程在队列中被处理的次序

优先级队列的实现

由于优先级队列与普通队列结构之间,只有插入数据时,需要考虑他的优先级,因此只需要修改上文实现的 Queue 类的 enqueue方法即可

代码

class PriorityQueueElement {
  element = undefined;
  priority = undefined;

  constructor(element, priority) {
    this.element = element;
    this.priority = priority;
  }
}

class PriorityQueue extends Queue{ // 继承上文实现的 Queue 类
  items: PriorityQueueElement[] = [];
  constructor() {
    super();
  }

  enqueue(element, priority) {
    const queueElement = new PriorityQueueElement(element, priority);

    if (this.isEmpty()) {
      this.items.push(queueElement);
    } else {
      let added = false
      for (let i = 0; i < this.items.length; i++) {
        if(queueElement.priority < this.items[i].priority) {
          this.items.splice(i, 0, queueElement)
          added = true
          break
        }
      }
      if (!added) {
        this.items.push(queueElement)
      }
    }
  }
}

测试代码

const priorityQueue = new PriorityQueue();
priorityQueue.enqueue('aaa', 29)
priorityQueue.enqueue('bbb', 21)
priorityQueue.enqueue('ccc', 10)

priorityQueue.enqueue('ddd', 14)
console.log(priorityQueue.items)
//[
//  PriorityQueueElement { element: 'ccc', priority: 10 },
//  PriorityQueueElement { element: 'ddd', priority: 14 },
//  PriorityQueueElement { element: 'bbb', priority: 21 },
//  PriorityQueueElement { element: 'aaa', priority: 29 }
//]