javascript数据结构与算法-队列结构

https://www.jianshu.com/p/ca1bb95ada76

队列

队列(Queue),它是一种运算受限的线性表,先进先出(FIFO First In First Out)
队列是一种受限的线性结构, 受限之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作

应用举例:线程队列

在开发中为了让任务可以并行处理通常会开启多个线程.
但不能让大量线程同时运行处理任务,会占用过多资源,因此使用线程队列.
线程队列会按照次序来启动线程并且处理对应的任务.

基于数组实现队列

// 自定义队列
function Queue() {
    var items = []

    // 队列操作的方法
    // enter queue方法
    this.enqueue = function (element) {
        items.push(element)
    }

    // delete queue方法
    this.dequeue = function () {
        return items.shift()
    }

    // 查看前端的元素
    this.front = function () {
        return items[0]
    }

    // 查看队列是否为空
    this.isEmpty = function () {
        return items.length == 0
    }

    // 查看队列中元素的个数
    this.size = function () {
        return items.length
    }
}

基于对象实现队列

创建类来表示一个队列.
使用对象存储元素, 使用count属性来控制队列大小, 使用lowestCount来追踪第一个元素.
然后声明一些队列可用的方法.

// @ts-check

export default class Queue {
  constructor() {
    this.count = 0; // 控制队列大小,队列大小计数
    this.lowestCount = 0; // 记录队列首项的下标值,用于追踪队列首项
    this.items = {}; // 使用对象存储元素
  }
  
  // 向队列尾部添加新的项
  enqueue(element) {
  	// items属性是对象,是键值对的集合
  	// 因此向队列尾部添加新的项可以把count变量作为items对象中的键key,对应的元素element作为它的值value.
    this.items[this.count] = element;
    this.count++;
  }
  
  // 移除队列首项并返回被移除的元素
  dequeue() {
  	// 若队列为空,返回undefined
    if (this.isEmpty()) {
      return undefined;
    }
    // 使用result暂存队列首项,以便delete移除队列首项后将其返回
    const result = this.items[this.lowestCount];
    delete this.items[this.lowestCount];
    // lowestCount 计数加 1
    this.lowestCount++;
    return result;
  }
  
  // 查看队列首项的元素
  peek() {
    if (this.isEmpty()) {
      return undefined;
    }
    return this.items[this.lowestCount];
  }
  
  // 检查队列是否为空
  isEmpty() {
    return this.size() === 0;
  }
  
  // 清空队列
  clear() {
    this.items = {};
    this.count = 0;
    this.lowestCount = 0;
  }
  
  // 获取队列长度
  size() {
  	// 计算队列中有多少元素,只需计算count和lowestCount之间的差值即可
  	// 比如count为2,lowestCount为0, 则说明队列中存在下标为0/1的两个元素
  	// 然后从队列中移除一个元素,lowestCount值变为1, count仍为2,则队列中只有下标为1的一个元素
    return this.count - this.lowestCount;
  }
  // 从索引值为lowestCount的位置开始迭代队列
  toString() {
    if (this.isEmpty()) {
      return '';
    }
    let objString = `${this.items[this.lowestCount]}`;
    for (let i = this.lowestCount + 1; i < this.count; i++) {
      objString = `${objString},${this.items[i]}`;
    }
    return objString;
  }
}

在这里插入图片描述

双端队列

允许从后端和前端 添加和移除元素的特殊队列
同时遵循先进先出和后进先出的原则, 可以说是队列和栈相结合的一种数据结构.
在这里插入图片描述
在这里插入图片描述

// @ts-check
// 双端队列结构
export default class Deque {
  constructor() {
    this.count = 0;
    this.lowestCount = 0;
    this.items = {};
  }
  // 在队列前端添加元素
  addFront(element) {
    if (this.isEmpty()) {
      this.addBack(element);
    } else if (this.lowestCount > 0) {
      this.lowestCount--;
      this.items[this.lowestCount] = element;
    } else { // this.lowestCount为0
      // 在第一位添加元素,需要将所有元素后移一位
      for (let i = this.count; i > 0; i--) {
        this.items[i] = this.items[i - 1];
      }
      this.count++;
      this.items[0] = element;
    }
  }
  // 在后端添加元素
  addBack(element) {
    this.items[this.count] = element;
    this.count++;
  }
  // 在前端删除并返回元素
  removeFront() {
    if (this.isEmpty()) {
      return undefined;
    }
    const result = this.items[this.lowestCount];
    delete this.items[this.lowestCount];
    this.lowestCount++;
    return result;
  }
  // 在后端删除并返回元素
  removeBack() {
    if (this.isEmpty()) {
      return undefined;
    }
    this.count--;
    const result = this.items[this.count];
    delete this.items[this.count];
    return result;
  }
  // 返回前端的第一个元素
  peekFront() {
    if (this.isEmpty()) {
      return undefined;
    }
    return this.items[this.lowestCount];
  }
  // 返回后端的第一个元素
  peekBack() {
    if (this.isEmpty()) {
      return undefined;
    }
    return this.items[this.count - 1];
  }

  isEmpty() {
    return this.size() === 0;
  }

  clear() {
    this.items = {};
    this.count = 0;
    this.lowestCount = 0;
  }

  size() {
    return this.count - this.lowestCount;
  }

  toString() {
    if (this.isEmpty()) {
      return '';
    }
    let objString = `${this.items[this.lowestCount]}`;
    for (let i = this.lowestCount + 1; i < this.count; i++) {
      objString = `${objString},${this.items[i]}`;
    }
    return objString;
  }
}

优先队列

优先级队列的特点:

普通的队列插入一个元素, 数据会被放在后端. 并且需要前面所有的元素都处理完成后才会处理前面的数据.
但是优先级队列, 在插入一个元素的时候会考虑该数据的优先级.(和其他数据优先级进行比较)
比较完成后, 可以得出这个元素正确的队列中的位置. 其他处理方式, 和队列的处理方式一样.
也就是说, 如果我们要实现优先级队列, 最主要是要修改添加方法. (当然, 还需要以某种方式来保存元素的优先级)

优先级队列的实现

实现优先级队列相对队列主要有两方面需要考虑:

封装元素和优先级放在一起(可以封装一个新的构造函数)
添加元素时, 将当前的优先级和队列中已经存在的元素优先级进行比较, 以获得自己正确的位置.

// 封装优先级队列
function PriorityQueue() {
    var items = []

    // 封装一个新的构造函数, 用于保存元素和元素的优先级
    function QueueElement(element, priority) {
        this.element = element
        this.priority = priority
    }

    // 添加元素的方法
    this.enqueue = function (element, priority) {
        // 1.根据传入的元素, 创建新的QueueElement
        var queueElement = new QueueElement(element, priority)

        // 2.获取传入元素应该在正确的位置
        if (this.isEmpty()) {
            items.push(queueElement)
        } else {
            var added = false
            for (var i = 0; i < items.length; i++) {
                // 注意: 我们这里是数字越小, 优先级越高
                if (queueElement.priority < items[i].priority) {
                    items.splice(i, 0, queueElement)
                    added = true
                    break
                }
            }

            // 遍历完所有的元素, 优先级都大于新插入的元素时, 就插入到最后
            if (!added) {
                items.push(queueElement)
            }
        }
    }

    // 删除元素的方法
    this.dequeue = function () {
        return items.shift()
    }

    // 获取前端的元素
    this.front = function () {
        return items[0]
    }

    // 查看元素是否为空
    this.isEmpty = function () {
        return items.length == 0
    }

    // 获取元素的个数
    this.size = function () {
        return items.length
    }
}

代码解析:

封装了一个QueueElement, 将element和priority封装在一起.
在插入新的元素时, 有如下情况下考虑:

  1. 根据新的元素先创建一个新的QueueElement对象.
  2. 如果元素是第一个被加进来的, 那么不需要考虑太多, 直接加入数组中即可.
  3. 如果是后面加进来的元素, 需要和前面加进来的元素依次对比优先级.
    一旦优先级, 大于某个元素, 就将该元素插入到元素这个元素的位置. 其他元素会依次向后移动.
  4. 如果遍历了所有的元素, 没有找到某个元素被这个新元素的优先级低, 直接放在最后即可.

作者:coderwhy
链接:https://www.jianshu.com/p/ca1bb95ada76
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

队列解决问题- 击鼓传花, 转圈报数

在这里插入图片描述

// 实现击鼓传花的函数
function passGame(nameList, num) {
    // 1.创建一个队列, 并且将所有的人放在队列中
    // 1.1.创建队列
    var queue = new Queue()

    // 1.2.通过for循环, 将nameList中的人放在队列中
    for (var i = 0; i < nameList.length; i++) {
        queue.enqueue(nameList[i])
    }

    // 2.寻找最后剩下的人
    while (queue.size() > 1) {
        // 将前num-1中的人, 都从队列的前端取出放在队列的后端
        for (var i = 0; i < num; i++) {
            queue.enqueue(queue.dequeue())
        }

        // 将第num个人, 从队列中移除
        queue.dequeue()
    }

    // 3.获取剩下的一个人
    alert(queue.size())
    var endName = queue.dequeue()
    alert("最终留下来的人:" + endName)

    // 4.获取该人在队列中的位置
    return nameList.indexOf(endName)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值