LeetCode题库之用栈实现队列

149 阅读4分钟

栈与队列

概念

在计算机科学中,队列(Queue)是一种常见的数据结构,其遵循先进先出(FIFO)的原则。栈(Stack)是一种线性数据结构,它遵循特定的原则:后进先出(LIFO)。

相同点

  1. 线性结构: 栈和队列都是线性数据结构,它们都以线性的方式存储元素。栈中的元素是垂直堆叠的,而队列中的元素是水平排列的。
  2. 数据存储方式: 栈和队列都使用特定的方式存储和访问数据。栈采用后进先出(LIFO)的原则,而队列采用先进先出(FIFO)的原则。
  3. 基本操作: 它们都有基本的操作,例如添加元素和移除元素。在栈中,添加元素被称为“压栈”(Push),移除元素被称为“出栈”(Pop)。而在队列中,添加元素被称为“入队”(Enqueue),移除元素被称为“出队”(Dequeue)。

不同点

  1. 操作方式:

    • 栈:遵循后进先出(LIFO)原则。最后进入栈的元素首先被移除。
    • 队列:遵循先进先出(FIFO)原则。最先进入队列的元素首先被移除。
  2. 主要操作:

    • 栈:主要操作是压栈(Push)和出栈(Pop)。压栈用于向栈顶添加元素,而出栈则移除并返回栈顶元素。
    • 队列:主要操作是入队(Enqueue)和出队(Dequeue)。入队用于在队列尾部添加元素,而出队则移除并返回队列头部元素。
  3. 用途和应用场景:

    • 栈:常见用途包括函数调用、表达式求值、回溯算法、内存管理(如函数调用栈)等。
    • 队列:常见用途包括任务调度、缓冲区管理、广度优先搜索算法(BFS)等。
  4. 数据访问方式:

    • 栈:只能通过栈顶访问和处理数据。
    • 队列:可以通过队首和队尾访问和处理数据。

用栈实现队列

今天我们将用LeetCode题库中的用栈实现队列来介绍如何使用栈(Stack)来模拟队列的行为,实现队列的基本功能。

队列数据存储结构

首先我们通过两个栈来模拟队列的行为:

const MyQueue = function () {
  this.stack1 = []; // 第一个栈用于入队操作
  this.stack2 = []; // 第二个栈用于出队操作
}

然后再通过下列操作实现队列行为:

  • 入队操作(push):将元素推入第一个栈 stack1
  • 出队操作(pop):如果 stack2 不为空,则直接从 stack2 弹出栈顶元素;如果 stack2 为空,则将 stack1 的元素逐个弹出并推入 stack2,然后从 stack2 弹出栈顶元素。
  • 获取队首元素(peek):同样需要考虑 stack2 的状态,若 stack2 为空,则先将 stack1 的元素推入 stack2,然后返回 stack2 的栈顶元素。
  • 判断队列是否为空(empty):当 stack1stack2 都为空时,队列为空。
MyQueue.prototype = {
  push: function (x) {

    this.statck1.push(x);
  },
  // FIFO
  pop: function () {// 从队列首部移除元素
    // 如果statck2为空,将statck1的元素pop到statck2中
    if (this.statck2.length <= 0) {
      while (this.statck1.length !== 0) {
        this.statck2.push(this.statck1.pop());// 将statck1的元素pop到statck2中
      }
    }
    return this.statck2.pop();// 如果statck2不为空,直接pop
  },
  // 拿到front的值
  peek: function () {
    if (this.statck2.length <= 0) {
      while (this.statck1.length !== 0) {
        this.statck2.push(this.statck1.pop());// 将statck1的元素pop到statck2中
      }
    }
    const stack2Len = this.statck2.length;
    return stack2Len && this.statck2[stack2Len - 1];// 如果statck2不为空,直接pop
  },
  // 判断队列是否为空
  empty: function () {
    return !this.statck1.length && !this.statck2.length; //如果statck1和statck2都为空,队列为空
  }
}

在23-24行中:

  1. const stack2Len = this.statck2.length;:这一行代码用于获取 statck2 栈的长度,并将长度值存储在 stack2Len 变量中。
  2. return stack2Len && this.statck2[stack2Len - 1];:在此处使用了逻辑与运算符 &&,在逻辑表达式中,如果 stack2Len 的值不为零(即 statck2 栈不为空),则返回 this.statck2[stack2Len - 1],即 statck2 栈顶元素。如果 stack2Len 的值为零(即 statck2 栈为空),则返回值为 false
  3. 这样的设计是为了避免对空栈进行 pop() 操作,因为在空栈上调用 pop() 方法将会导致错误。在 peek() 方法中,首先检查 stack2Len 的值是否为真(即是否大于零),如果为真,则返回 statck2 栈顶元素,否则返回 false

结论

利用两个栈的特性,我们成功地实现了队列的基本操作,包括入队、出队、获取队首元素以及判断队列是否为空。因此这个LeetCode问题也就解决了。