JavaScript 数据结构和算法——队列

204 阅读2分钟

JavaScript 数据结构和算法——队列

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 2 天,点击查看活动详情

介绍

队列和栈非常的类似,队列是一种先进先出的线性结构,生活当中类似的场景比如排队打饭,排队买票等。最先的那个人先得到服务,也称为先来先服务。在业务场景当中,我们经常会有发多个网络请求的情况,其中某个请求必须等前一个请求的数据,这时候我们可以实现一个队列来实现业务需求,当第一个请求请求回调会来,我们再调用下一个请求,依次类推,调用完所有的请求。

思路

队列的实现思路其实和栈很类似,只不过增加元素和删除元素的逻辑有改变。我们在对象当中依次添加属性,然后通过两个变量top、bottom来记录当前的队列首元素和队列尾元素。通过top记录当前首元素的key值,后续我们在获取首元素的时候,只需要通过top来获取就好了,而bottom则记录尾元素的后一位元素,当我们需要获取尾元素的时候,我们直接通过bottom - 1就可以获取当前队列的尾元素了。如果需要添加元素,则直接添加到bottom的位置上就可以了,然后bottom++

代码实现

//创建Queueclass Queue {
  constructor() {
    //指向队列的首元素
    this.top = 0;
    //指向队列的尾元素
    this.bottom = 0;
    //创建队列对象
    this.items = {};
  }
​
  //push增减队列元素
  push(ele) {
    this.items[this.bottom] = ele;
    //尾部加一
    this.bottom++;
  }
​
  //删除首元素
  shift() {
    if (this.isEmpty()) {
      return;
    }
​
    let result = this.items[this.top];
    delete this.items[this.top];
​
    //首元素下标位移,总长度减少
    this.top++;
    return result;
  }
​
  //判断队列是否为空
  isEmpty() {
    return this.bottom - this.top === 0;
  }
​
  //返回队列长度
  size() {
    return this.bottom - this.top;
  }
  //返回队列首元素
  peek() {
    return this.items[this.top];
  }
​
  //清空队列
  clear() {
    //初始化队列
    this.items = {};
    this.count = 0;
    this.top = 0;
    this.bottom = 0;
  }
  // 转成字符串
  toString() {
    let result = ``;
    for (let i = this.top; i <= this.bottom - 1; i++) {
      result += this.items[i] + ",";
    }
    return result;
  }
}
​
const queue = new Queue();
queue.push("jack");
queue.push("mark");
queue.push("lazy");
console.log(queue.size()); // 3
console.log(queue.shift()); // jack
console.log(queue.size()); //2
console.log(queue.toString()); // jmark,lazy,

双端队列介绍

双端队列顾名思义就是两端都可以增加删除元素。有点像栈和队列的集合。双端队列在现实生活当中的例子有电影院、餐厅中排队,你买完了票离开了队首,但是你还有一些东西需要咨询一下,于是你直接走到的队伍的首位,这时候就在队列的首元素前增加的一个元素。或者在队伍尾部的人如果不想买票了,那么他也可以离开队列。双端队列比较灵活。在日常的计算机科学当中,双端队列的一个常见的应用技术存储一些列的撤销操作,当你想撤销你的撤销操作的时候,就可以把撤销操作从撤销队列当中取出。

双端队列思路

按照这个思路,我们可以结合栈的方法来实现双端队列。首先定义一个{}来保存双端队列当中的元素。然后定义两个变量top、bottom来记录队列的首元素和尾元素。定义push、pop、shift、unshift方法来实现队列尾增减元素、队列首增减元素。push方法通过获取bottom的指向,向{}当中添加值,同时bottom++pop方法通过bottom - 1来获取当前队列的最后一位,通过delete删除这一属性。shift方法通过top来获取首位元素,然后delete这一属性。unshift方法通过将队列当中的元素往后移一位,将前面的首位空出来,然后将新增加的元素添加到首位上。

双端队列实现

//创建双端队列
class PrivateAttr<T> {
  public items: {
    [propName: number]: T;
  };
  public top: number;
  public bottom: number;
}
​
interface DeQueTypes<T> extends PrivateAttr<T> {
  unshift(ele: T);
  push(ele: T);
  pop(): T;
  shift(): T;
  isEmpty(): boolean;
  size(): number;
  toString(): string;
  peekTop(): number;
  peekBottom(): number;
}
​
class DeQue extends PrivateAttr<number> implements DeQueTypes<number> {
  constructor() {
    super();
    this.items = {};
    this.top = 0;
    this.bottom = 0;
  }
​
  //移除首部元素
  shift(): number {
    let result = this.items[this.top];
    delete this.items[this.top];
    this.top++;
    return result;
  }
​
  //在首部添加元素
  unshift(ele: number) {
    for (let i = this.bottom; i >= this.top; i--) {
      this.items[i] = this.items[i - 1];
    }
    this.items[this.top] = ele;
  }
​
  //在尾部添加元素
  push(ele: number) {
    this.items[this.bottom] = ele;
    this.bottom++;
  }
​
  //删除尾部元素
  pop(): number {
    let result = this.items[this.bottom];
    delete this.items[this.bottom];
    this.bottom--;
    return result;
  }
​
  //返回队列是否为空
  isEmpty(): boolean {
    return this.bottom - this.top === 0;
  }
​
  //   返回队列长度
  size(): number {
    return this.bottom - this.top;
  }
​
  //队列字符串
  toString(): string {
    let result = ``;
    for (let i = this.top; i < this.bottom - 1; i++) {
      result += this.items[i] + ",";
    }
​
    return result;
  }
​
  //返回首端元素
  peekTop(): number {
    console.log(this.items);
    return this.items[this.top];
  }
  //   返回尾端元素
  peekBottom(): number {
    return this.items[this.bottom - 1];
  }
}
​
const deQue = new DeQue();