JavaScript 实现经典数据结构之栈和队列

186 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

数据结构分类

传统数据结构分为以下几类:

  • 数组 Array
  • 堆栈:Stack
  • 队列:Queue
  • 链表:Linked Lists
  • 树:Trees
  • 图:Graphs
  • 散列表(哈希表):Hash Tables

栈和队列

堆栈和队列是一种操作受限的线性结构,JavaScript 中没有内置这样的数据结构,因此我们来实现下。

栈是一种操作受限的线性结构,限定只能在尾部进行插入和删除操作,尾部被称为栈顶,而头部称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,从一个栈删除元素又称作出栈或退栈。这种受限的操作方式让栈元素的入栈出栈遵循一种特殊的原则——先进后出(First In Last Out,FILO)。

栈的应用非常广泛,这里列举 3 种:

  • 浏览器的历史记录,它的前进、后退功能就是一个栈操作;
  • V8 中的函数执行过程采用的栈结构;
  • JavaScript 在捕获代码异常时,详细信息会以调用栈的形式打印。

栈可以通过数组来实现,下面的代码实现了一个栈结构:

class Stack {
  constructor(...args) {
    // 使用数组进行模拟
    this.stack = [...args]
  }
  push(...items) {
    // 入栈
    return this.stack.push(... items)
  }
  pop() {
    // 出栈,从数组尾部弹出一项
    return this.stack.pop()
  }
  peek() {
    return this.isEmpty() 
        ? undefined
        : this.stack[this.size() - 1]
  }
  isEmpty() {
    return this.size() == 0
  }
  size() {
    return this.stack.length
  }
}

队列

队列和栈一样也是操作受限的线性结构,但和栈有所区别的是,队列可以在头部和尾部进行操作,但尾部只能插入,头部只能删除。这种受限的操作方式让队列元素的插入和删除遵循一种特殊的原则——先进先出原则(First In First Out,FIFO)。

JavaScript 在处理异步操作时经常会用到队列,比如宏任务队列、微任务队列、回调函数队列。

队列的实现也可以通过数组来实现,下面的代码实现了一个队列结构:

class Queue {
  constructor(...args) {
    // 使用数组进行模拟
    this.queue = [...args]
  }
  enqueue(...items) {
    // 入队
    return this.queue.push(... items)
  }
  dequeue() {
    // 出队
    return this.queue.shift()
  }
  front() { 
    return this.isEmpty()
        ? undefined
        : this.queue[0]
  }
  back() {
    return this.isEmpty()
        ? undefined
        : this.queue[this.size() - 1]
  }
  isEmpty() {
    return this.size() == 0
  }
  size() {
    return this.queue.length
  }
}

最后说一句

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下,您的支持是我坚持写作最大的动力,多谢支持。