ChatGPT解决这个技术问题 Extra ChatGPT

如何在 JavaScript 中实现堆栈和队列?

在 JavaScript 中实现堆栈和队列的最佳方法是什么?

我正在寻找做调车场算法,我将需要这些数据结构。

作为循环缓冲区

J
John
var stack = [];
stack.push(2);       // stack is now [2]
stack.push(5);       // stack is now [2, 5]
var i = stack.pop(); // stack is now [2]
alert(i);            // displays 5

var queue = [];
queue.push(2);         // queue is now [2]
queue.push(5);         // queue is now [2, 5]
var i = queue.shift(); // queue is now [5]
alert(i);              // displays 2

取自“9 JavaScript Tips You May Not Know


我建议谨慎使用 queue.shift。 IIRC 它不是 O(1),而是 O(n),如果队列变大,可能会太慢。
我会说这取决于javascript实现。我认为它没有在 javascript 规范中定义。
Array.join() 现在也比 JS 中通常的 '+' 慢,这是因为 Array.join() 没有收到尽可能多的优化更新,而 '+' 会...我会研究所有这些提示在使用它们之前
ImmutableJs 是所有类型不可变数据(包括堆栈)的绝佳库。是的,这是 O(1) 效率。 facebook.github.io/immutable-js/docs/#/Stack
实际上是有的。这是 SSL 证书和根域以及 Ghost 博客的问题。
C
Community

Javascript 有 push 和 pop 方法,它们对普通的 Javascript 数组对象进行操作。

对于队列,请看这里:

http://safalra.com/web-design/javascript/queues/

队列可以在 JavaScript 中使用数组对象的 push 和 shift 方法或 unshift 和 pop 方法来实现。虽然这是一种实现队列的简单方法,但对于大型队列来说效率非常低——因为方法对数组进行操作,每次调用 shift 和 unshift 方法时都会移动数组中的每个元素。 Queue.js 是一个简单高效的 JavaScript 队列实现,它的出队函数在摊销的常数时间内运行。因此,对于较大的队列,它可能比使用数组快得多。


您共享的链接具有检查基准测试结果的功能,并且在使用 Google Chrome 版本 59 进行测试时,我没有看到性能提升。Queue.js 与其速度不一致,但 Chrome 与其速度一致。
我还用 queue.js 做了一个演示,dequeue 函数并没有真正从队列中删除项目,所以我想知道它是否假设它是如何工作的?如果是这样,在出列前一个项目后如何检索新队列? codepen.io/adamchenwei/pen/VxgNrX?editors=0001
看起来 queue.js 中的出队也需要额外的内存,因为它正在使用切片克隆数组。
此外,随着每个添加元素,底层数组变得越来越大。即使实现不时减小数组大小,总体大小也会增加。
J
Jani Hartikainen

数组。

堆:

var stack = [];

//put value on top of stack
stack.push(1);

//remove value from top of stack
var value = stack.pop();

队列:

var queue = [];

//put value on end of queue
queue.push(1);

//Take first value from queue
var value = queue.shift();

Array.prototype.pop 不会从 Array 的顶部(第一个元素)移除值。它从数组的底部(最后一个元素)中删除值。
@MichaelGeller 堆栈的顶部是数组的最后一个元素。数组 push 和 pop 方法的行为就像一个堆栈。
@mrdommyg Array.prototype.pop 删除数组的 last 元素(参见 developer.mozilla.org/en/docs/Web/JavaScript/Reference/…)。在这种情况下,最后是指具有最高索引的元素。 JS 中的数组与栈无关。它不是堆栈,因为它有一个 pop 方法。 Pop 只是意味着“删除最后一个元素并返回它”。当然,您可以用数组模仿堆栈的功能,但数组仍然不是定义的堆栈。它仍然是一个列表(根据 MDN,它是一个“类似列表”的对象)。
@MichaelGeller 堆栈的行为是“先进后出”。如果您使用 JavaScript 中的数组及其 pushpop 方法来实现它,那么问题就解决了。我真的不明白你的意思。
@MichaelGeller 堆栈是概念性的。 JS 数组(除其他外)通过实现堆栈语义而定义为堆栈。仅仅因为它还实现了数组语义并不会改变这一点。您可以使用 JS 数组,例如开箱即用的堆栈,在这种情况下,您推送和弹出的是“顶部”元素。
u
user2954463

如果您想制作自己的数据结构,您可以构建自己的:

var Stack = function(){
  this.top = null;
  this.size = 0;
};

var Node = function(data){
  this.data = data;
  this.previous = null;
};

Stack.prototype.push = function(data) {
  var node = new Node(data);

  node.previous = this.top;
  this.top = node;
  this.size += 1;
  return this.top;
};

Stack.prototype.pop = function() {
  temp = this.top;
  this.top = this.top.previous;
  this.size -= 1;
  return temp;
};

对于队列:

var Queue = function() {
  this.first = null;
  this.size = 0;
};

var Node = function(data) {
  this.data = data;
  this.next = null;
};

Queue.prototype.enqueue = function(data) {
  var node = new Node(data);

  if (!this.first){
    this.first = node;
  } else {
    n = this.first;
    while (n.next) {
      n = n.next;
    }
    n.next = node;
  }

  this.size += 1;
  return node;
};

Queue.prototype.dequeue = function() {
  temp = this.first;
  this.first = this.first.next;
  this.size -= 1;
  return temp;
};

为了避免需要遍历整个事物以追加到末尾,请通过 this.last=node; 存储对最后一个的引用;
除非你有充分的理由,否则永远不要实现这样的任何队列……虽然它在逻辑上看起来是正确的,但 CPU 并不根据人类的抽象来运行。迭代一个到处都有指针的数据结构将导致 CPU 中的缓存未命中,这与高效的顺序数组不同。 blog.davidecoppola.com/2014/05/… CPU 非常讨厌指针 - 它们可能是导致缓存未命中和必须从 RAM 访问内存的第一大原因。
这是一个诱人的解决方案,但我没有看到在弹出/出列时创建的 Node 被删除......他们不会只是坐在那里占用内存直到浏览器崩溃吗?
@cneuro 与 C++ 不同,JavaScript 是一种垃圾收集语言。它有一个 delete 关键字,但它只对 mark a property of an object as being non-present—which is different from just assigning undefined to the property 有用。 JavaScript 也有一个 new 运算符,但它只是用于在调用函数时将 this 设置为一个新的空对象。在 C++ 中,您需要将每个 newdelete 配对,但在 JavaScript 中则不需要,因为 GC。要停止在 JavaScript 中使用内存,只需停止引用该对象,它最终会被回收。
是否也有必要通过设置最大堆栈大小来检查堆栈是否溢出?
g
ggorlen

这是我使用链表实现的堆栈和队列:

// 链表函数 Node(data) { this.data = data; this.next = null; } // 使用 LinkedList 实现的栈 function Stack() { this.top = null; } Stack.prototype.push = function(data) { var newNode = new Node(data); newNode.next = this.top; //特别注意 this.top = newNode; } Stack.prototype.pop = function() { if (this.top !== null) { var topItem = this.top.data; this.top = this.top.next;返回顶部项目; } 返回空值; } Stack.prototype.print = function() { var curr = this.top; while (curr) { console.log(curr.data);当前=当前。下一个; } } // var stack = new Stack(); // stack.push(3); // stack.push(5); // stack.push(7); // stack.print(); // 使用 LinkedList 实现的队列 function Queue() { this.head = null; this.tail = null; } Queue.prototype.enqueue = function(data) { var newNode = new Node(data); if (this.head === null) { this.head = newNode; this.tail = newNode; } else { this.tail.next = newNode; this.tail = newNode; } } Queue.prototype.dequeue = function() { var newNode; if (this.head !== null) { newNode = this.head.data; this.head = this.head.next; } 返回新节点; } Queue.prototype.print = function() { var curr = this.head; while (curr) { console.log(curr.data);当前=当前。下一个; } } var queue = new Queue(); queue.enqueue(3); queue.enqueue(5); queue.enqueue(7); queue.print(); queue.dequeue(); queue.dequeue(); queue.print();


k
kevinyu

Javascript 数组 shift() 很慢,尤其是在保存许多元素时。我知道两种方法来实现具有摊销 O(1) 复杂度的队列。

首先是使用循环缓冲区和表加倍。我之前已经实现了这一点。您可以在此处查看我的源代码 https://github.com/kevyuu/rapid-queue

第二种方法是使用两个堆栈。这是具有两个堆栈的队列的代码

function createDoubleStackQueue() {
var that = {};
var pushContainer = [];
var popContainer = [];

function moveElementToPopContainer() {
    while (pushContainer.length !==0 ) {
        var element = pushContainer.pop();
        popContainer.push(element);
    }
}

that.push = function(element) {
    pushContainer.push(element);
};

that.shift = function() {
    if (popContainer.length === 0) {
        moveElementToPopContainer();
    }
    if (popContainer.length === 0) {
        return null;
    } else {
        return popContainer.pop();
    }
};

that.front = function() {
    if (popContainer.length === 0) {
        moveElementToPopContainer();
    }
    if (popContainer.length === 0) {
        return null;
    }
    return popContainer[popContainer.length - 1];
};

that.length = function() {
    return pushContainer.length + popContainer.length;
};

that.isEmpty = function() {
    return (pushContainer.length + popContainer.length) === 0;
};

return that;}

这是使用 jsPerf 的性能比较

CircularQueue.shift() 与 Array.shift()

http://jsperf.com/rapidqueue-shift-vs-array-shift

如您所见,使用大型数据集时速度明显更快


哪个更快?您的 jsperf 链接不起作用。
N
Niroshan Ratnayake

正如其他答案中所解释的,堆栈实现是微不足道的。

但是,我在这个线程中没有找到任何令人满意的答案,用于在 javascript 中实现队列,所以我自己做了。

此线程中有三种类型的解决方案:

数组 - 最糟糕的解决方案,在大型数组上使用 array.shift() 效率非常低。

链表 - 它是 O(1),但为每个元素使用一个对象有点过分,特别是如果它们很多而且它们很小,比如存储数字。

延迟移位数组 - 它包括将索引与数组相关联。当一个元素出队时,索引向前移动。当索引到达数组的中间时,数组被分成两部分以删除前半部分。

延迟移位数组是我心目中最令人满意的解决方案,但它们仍然将所有内容存储在一个大的连续数组中,这可能会出现问题,并且当数组被切片时应用程序将交错。

我使用小数组的链表(每个最多 1000 个元素)进行了实现。数组的行为类似于延迟移位数组,只是它们从不切片:当数组中的每个元素都被删除时,数组就会被简单地丢弃。

该软件包是on npm,具有基本的 FIFO 功能,我最近才推送它。代码分为两部分。

这是第一部分

/** Queue contains a linked list of Subqueue */
class Subqueue <T> {
  public full() {
    return this.array.length >= 1000;
  }

  public get size() {
    return this.array.length - this.index;
  }

  public peek(): T {
    return this.array[this.index];
  }

  public last(): T {
    return this.array[this.array.length-1];
  }

  public dequeue(): T {
    return this.array[this.index++];
  }

  public enqueue(elem: T) {
    this.array.push(elem);
  }

  private index: number = 0;
  private array: T [] = [];

  public next: Subqueue<T> = null;
}

这是主要的 Queue 类:

class Queue<T> {
  get length() {
    return this._size;
  }

  public push(...elems: T[]) {
    for (let elem of elems) {
      if (this.bottom.full()) {
        this.bottom = this.bottom.next = new Subqueue<T>();
      }
      this.bottom.enqueue(elem);
    }

    this._size += elems.length;
  }

  public shift(): T {
    if (this._size === 0) {
      return undefined;
    }

    const val = this.top.dequeue();
    this._size--;
    if (this._size > 0 && this.top.size === 0 && this.top.full()) {
      // Discard current subqueue and point top to the one after
      this.top = this.top.next;
    }
    return val;
  }

  public peek(): T {
    return this.top.peek();
  }

  public last(): T {
    return this.bottom.last();
  }

  public clear() {
    this.bottom = this.top = new Subqueue();
    this._size = 0;
  }

  private top: Subqueue<T> = new Subqueue();
  private bottom: Subqueue<T> = this.top;
  private _size: number = 0;
}

可以轻松删除类型注释 (: X) 以获得 ES6 javascript 代码。


E
Eugen Sunic

您可以根据概念使用自己的自定义类,这里是您可以用来做这些事情的代码片段

/*
*   Stack implementation in JavaScript
*/



function Stack() {
  this.top = null;
  this.count = 0;

  this.getCount = function() {
    return this.count;
  }

  this.getTop = function() {
    return this.top;
  }

  this.push = function(data) {
    var node = {
      data: data,
      next: null
    }

    node.next = this.top;
    this.top = node;

    this.count++;
  }

  this.peek = function() {
    if (this.top === null) {
      return null;
    } else {
      return this.top.data;
    }
  }

  this.pop = function() {
    if (this.top === null) {
      return null;
    } else {
      var out = this.top;
      this.top = this.top.next;
      if (this.count > 0) {
        this.count--;
      }

      return out.data;
    }
  }

  this.displayAll = function() {
    if (this.top === null) {
      return null;
    } else {
      var arr = new Array();

      var current = this.top;
      //console.log(current);
      for (var i = 0; i < this.count; i++) {
        arr[i] = current.data;
        current = current.next;
      }

      return arr;
    }
  }
}

并使用您的控制台检查这一点,并一一尝试这些行。

>> var st = new Stack();

>> st.push("BP");

>> st.push("NK");

>> st.getTop();

>> st.getCount();

>> st.displayAll();

>> st.pop();

>> st.displayAll();

>> st.getTop();

>> st.peek();

否决命名约定:以大写字母开头的方法假定为构造函数。
A
Anish K.

有很多方法可以在 Javascript 中实现堆栈和队列。上面的大多数答案都是相当肤浅的实现,我会尝试实现一些更易读(使用 es6 的新语法特性)和健壮的东西。

这是堆栈实现:

class Stack {
  constructor(...items){
    this._items = []

    if(items.length>0)
      items.forEach(item => this._items.push(item) )

  }

  push(...items){
    //push item to the stack
     items.forEach(item => this._items.push(item) )
     return this._items;

  }

  pop(count=0){
    //pull out the topmost item (last item) from stack
    if(count===0)
      return this._items.pop()
     else
       return this._items.splice( -count, count )
  }

  peek(){
    // see what's the last item in stack
    return this._items[this._items.length-1]
  }

  size(){
    //no. of items in stack
    return this._items.length
  }

  isEmpty(){
    // return whether the stack is empty or not
    return this._items.length==0
  }

  toArray(){
    return this._items;
  }
}

这就是您可以使用堆栈的方式:

let my_stack = new Stack(1,24,4);
// [1, 24, 4]
my_stack.push(23)
//[1, 24, 4, 23]
my_stack.push(1,2,342);
//[1, 24, 4, 23, 1, 2, 342]
my_stack.pop();
//[1, 24, 4, 23, 1, 2]
my_stack.pop(3)
//[1, 24, 4]
my_stack.isEmpty()
// false
my_stack.size();
//3

如果您想查看有关此实现的详细说明以及如何进一步改进它,可以在此处阅读:http://jschap.com/data-structures-in-javascript-stack/

这是 es6 中队列实现的代码:

class Queue{
 constructor(...items){
   //initialize the items in queue
   this._items = []
   // enqueuing the items passed to the constructor
   this.enqueue(...items)
 }

  enqueue(...items){
    //push items into the queue
    items.forEach( item => this._items.push(item) )
    return this._items;
  }

  dequeue(count=1){
    //pull out the first item from the queue
    this._items.splice(0,count);
    return this._items;
  }

  peek(){
    //peek at the first item from the queue
    return this._items[0]
  }

  size(){
    //get the length of queue
    return this._items.length
  }

  isEmpty(){
    //find whether the queue is empty or no
    return this._items.length===0
  }
}

以下是如何使用此实现:

let my_queue = new Queue(1,24,4);
// [1, 24, 4]
my_queue.enqueue(23)
//[1, 24, 4, 23]
my_queue.enqueue(1,2,342);
//[1, 24, 4, 23, 1, 2, 342]
my_queue.dequeue();
//[24, 4, 23, 1, 2, 342]
my_queue.dequeue(3)
//[1, 2, 342]
my_queue.isEmpty()
// false
my_queue.size();
//3

要阅读有关如何实现这些数据结构以及如何进一步改进这些数据结构的完整教程,您可能需要阅读 jschap.com 上的“使用 javascript 中的数据结构”系列。这是队列的链接 - http://jschap.com/playing-data-structures-javascript-queues/


N
Ni3

或者你可以使用两个数组来实现队列数据结构。

var temp_stack = new Array();
var stack = new Array();

temp_stack.push(1);
temp_stack.push(2);
temp_stack.push(3);

如果我现在弹出元素,那么输出将是 3,2,1。但我们需要 FIFO 结构,因此您可以执行以下操作。

stack.push(temp_stack.pop());
stack.push(temp_stack.pop());
stack.push(temp_stack.pop());

stack.pop(); //Pop out 1
stack.pop(); //Pop out 2
stack.pop(); //Pop out 3

仅当您在第一次 pop 后从未push 时才有效
A
Arijit Basu
/*------------------------------------------------------------------ 
 Defining Stack Operations using Closures in Javascript, privacy and
 state of stack operations are maintained

 @author:Arijt Basu
 Log: Sun Dec 27, 2015, 3:25PM
 ------------------------------------------------------------------- 
 */
var stackControl = true;
var stack = (function(array) {
        array = [];
        //--Define the max size of the stack
        var MAX_SIZE = 5;

        function isEmpty() {
            if (array.length < 1) console.log("Stack is empty");
        };
        isEmpty();

        return {

            push: function(ele) {
                if (array.length < MAX_SIZE) {
                    array.push(ele)
                    return array;
                } else {
                    console.log("Stack Overflow")
                }
            },
            pop: function() {
                if (array.length > 1) {
                    array.pop();
                    return array;
                } else {
                    console.log("Stack Underflow");
                }
            }

        }
    })()
    // var list = 5;
    // console.log(stack(list))
if (stackControl) {
    console.log(stack.pop());
    console.log(stack.push(3));
    console.log(stack.push(2));
    console.log(stack.pop());
    console.log(stack.push(1));
    console.log(stack.pop());
    console.log(stack.push(38));
    console.log(stack.push(22));
    console.log(stack.pop());
    console.log(stack.pop());
    console.log(stack.push(6));
    console.log(stack.pop());
}
//End of STACK Logic

/* Defining Queue operations*/

var queue = (function(array) {
    array = [];
    var reversearray;
    //--Define the max size of the stack
    var MAX_SIZE = 5;

    function isEmpty() {
        if (array.length < 1) console.log("Queue is empty");
    };
    isEmpty();

    return {
        insert: function(ele) {
            if (array.length < MAX_SIZE) {
                array.push(ele)
                reversearray = array.reverse();
                return reversearray;
            } else {
                console.log("Queue Overflow")
            }
        },
        delete: function() {
            if (array.length > 1) {
                //reversearray = array.reverse();
                array.pop();
                return array;
            } else {
                console.log("Queue Underflow");
            }
        }
    }



})()

console.log(queue.insert(5))
console.log(queue.insert(3))
console.log(queue.delete(3))

M
Mayank Narula

我喜欢认为实现堆栈和队列的最干净的方法应该是使用一个允许从两端添加和删除的容器,然后限制其一端的功能,这可以通过 Javascript 中的一个简单数组来完成。

// 封装时在堆栈容器中使用的语句

// Allow push and pop from the same end
array.push(element);
array.pop();

// 封装时队列容器中使用的语句

// Allow push and pop from different ends
array.push(element);
array.shift();

s
snydergd

这是一个相当简单的队列实现,有两个目标:

与 array.shift() 不同,您知道这种出队方法需要恒定时间 (O(1))。

为了提高速度,这种方法使用的分配比链表方法少得多。

堆栈实现仅共享第二个目标。

// Queue
function Queue() {
        this.q = new Array(5);
        this.first = 0;
        this.size = 0;
}
Queue.prototype.enqueue = function(a) {
        var other;
        if (this.size == this.q.length) {
                other = new Array(this.size*2);
                for (var i = 0; i < this.size; i++) {
                        other[i] = this.q[(this.first+i)%this.size];
                }
                this.first = 0;
                this.q = other;
        }
        this.q[(this.first+this.size)%this.q.length] = a;
        this.size++;
};
Queue.prototype.dequeue = function() {
        if (this.size == 0) return undefined;
        this.size--;
        var ret = this.q[this.first];
        this.first = (this.first+1)%this.q.length;
        return ret;
};
Queue.prototype.peek = function() { return this.size > 0 ? this.q[this.first] : undefined; };
Queue.prototype.isEmpty = function() { return this.size == 0; };

// Stack
function Stack() {
        this.s = new Array(5);
        this.size = 0;
}
Stack.prototype.push = function(a) {
        var other;
    if (this.size == this.s.length) {
            other = new Array(this.s.length*2);
            for (var i = 0; i < this.s.length; i++) other[i] = this.s[i];
            this.s = other;
    }
    this.s[this.size++] = a;
};
Stack.prototype.pop = function() {
        if (this.size == 0) return undefined;
        return this.s[--this.size];
};
Stack.prototype.peek = function() { return this.size > 0 ? this.s[this.size-1] : undefined; };

J
Javier Giovannini

如果您了解带有 push() 和 pop() 函数的堆栈,那么 queue 只是在相反的意义上进行这些操作之一。 push() 的对面是 unshift() 和 pop() es shift() 的对面。然后:

//classic stack
var stack = [];
stack.push("first"); // push inserts at the end
stack.push("second");
stack.push("last");
stack.pop(); //pop takes the "last" element

//One way to implement queue is to insert elements in the oposite sense than a stack
var queue = [];
queue.unshift("first"); //unshift inserts at the beginning
queue.unshift("second");
queue.unshift("last");
queue.pop(); //"first"

//other way to do queues is to take the elements in the oposite sense than stack
var queue = [];
queue.push("first"); //push, as in the stack inserts at the end
queue.push("second");
queue.push("last");
queue.shift(); //but shift takes the "first" element

给那些编写性能关键软件的人一个警告。 .shift() 方法不是正确的队列实现。它是 O(n) 而不是 O(1),对于大型队列来说会很慢。
谢谢@RudiKershaw,你是对的。如果需要实现 O(1) 队列,可以使用链表构建。
R
Redu

有点晚的答案,但我认为这个答案应该在这里。这是使用稀疏数组幂的 O(1) enqueue 和 O(1) dequeue 队列的实现。

JS 中的稀疏数组大多被忽视,但它们实际上是一颗宝石,我们应该在一些关键任务中使用它们的力量。

所以这里是一个骨架 Queue 实现,它扩展了 Array 类型,并且一直在 O(1) 中完成。

类队列扩展数组 { constructor(){ super() Object.defineProperty(this,"head",{ value : 0 , writable: true }); } enqueue(x) { this.push(x);返回这个; } dequeue() { var first;返回 this.head < this.length ? ( first = this[this.head] , delete this[this.head++] , first ) : void 0; // 完美的未定义 } peek() { return this[this.head]; } } var q = 新队列();控制台.log(q.dequeue()); // 不会破坏 console.log(q.enqueue(10)); // 添加 10 console.log(q.enqueue("DIO")); // 添加 "DIO" (Last In Line cCc RJDIO reis cCc) console.log(q); // 显示 q console.log(q.dequeue()); // 让我们得到第一个 console.log(q.dequeue()); // 让 DIO 从行 .as-console-wrapper { max-height: 100% !important; }

那么我们这里有潜在的内存泄漏吗?不,我不这么认为。 JS 稀疏数组是不连续的。因此,已删除的项目不应成为阵列内存占用的一部分。让 GC 为您完成它的工作。它是免费的。

一个潜在的问题是,当您不断将项目排入队列时,length 属性会无限增长。然而,一旦长度达到某个值,仍然可以实现一种自动刷新(压缩)机制来启动。

编辑:

上面的代码很好,但 delete 运算符虽然仍然是 O(1),但速度很慢。除了现代 JS 引擎进行了如此优化之外,对于 like <大约 25000 个项目 .shift() 无论如何都工作 O(1)。所以我们需要更好的东西。

在这种特殊情况下,随着发动机的发展,我们必须利用它们的新能力。下面的代码使用链表,我相信它是截至 2021 年最快、最安全的现代 JS 队列结构。

class Queue {
  #head;
  #last;
  constructor(){
    this.#head;
    this.#last;
  };
  enqueue(value){
    var link = {value, next: void 0};
    this.#last = this.#head ? this.#last.next = link
                            : this.#head      = link;
  }
  dequeue(){
    var first;
    return this.#head && ( first = this.#head.value
                         , this.#head = this.#head.next
                         , first
                         );
  }
  peek(){
    return this.#head && this.#head.value;
  }
};

这是一个非常快速的队列结构,并使用 Private Class Fields 隐藏关键变量以防窥探。


p
protoEvangelion

如果您正在寻找具有一些基本操作(基于链表)的堆栈和队列数据结构的 ES6 OOP 实现,那么它可能如下所示:

队列.js

import LinkedList from '../linked-list/LinkedList';

export default class Queue {
  constructor() {
    this.linkedList = new LinkedList();
  }

  isEmpty() {
    return !this.linkedList.tail;
  }

  peek() {
    if (!this.linkedList.head) {
      return null;
    }

    return this.linkedList.head.value;
  }

  enqueue(value) {
    this.linkedList.append(value);
  }

  dequeue() {
    const removedHead = this.linkedList.deleteHead();
    return removedHead ? removedHead.value : null;
  }

  toString(callback) {
    return this.linkedList.toString(callback);
  }
}

堆栈.js

import LinkedList from '../linked-list/LinkedList';

export default class Stack {
  constructor() {
    this.linkedList = new LinkedList();
  }

  /**
   * @return {boolean}
   */
  isEmpty() {
    return !this.linkedList.tail;
  }

  /**
   * @return {*}
   */
  peek() {
    if (!this.linkedList.tail) {
      return null;
    }

    return this.linkedList.tail.value;
  }

  /**
   * @param {*} value
   */
  push(value) {
    this.linkedList.append(value);
  }

  /**
   * @return {*}
   */
  pop() {
    const removedTail = this.linkedList.deleteTail();
    return removedTail ? removedTail.value : null;
  }

  /**
   * @return {*[]}
   */
  toArray() {
    return this.linkedList
      .toArray()
      .map(linkedListNode => linkedListNode.value)
      .reverse();
  }

  /**
   * @param {function} [callback]
   * @return {string}
   */
  toString(callback) {
    return this.linkedList.toString(callback);
  }
}

上面示例中用于 Stack 和 Queue 的 LinkedList 实现可以在 on GitHub here 中找到。


A
Andriy

没有数组

//Javascript stack linked list data structure (no array)

function node(value, noderef) {
    this.value = value;
    this.next = noderef;
}
function stack() {
    this.push = function (value) {
        this.next = this.first;
        this.first = new node(value, this.next);
    }
    this.pop = function () {
        var popvalue = this.first.value;
        this.first = this.first.next;
        return popvalue;
    }
    this.hasnext = function () {
        return this.next != undefined;
    }
    this.isempty = function () {
        return this.first == undefined;
    }

}

//Javascript stack linked list data structure (no array)
function node(value, noderef) {
    this.value = value;
    this.next = undefined;
}
function queue() {
    this.enqueue = function (value) {
        this.oldlast = this.last;
        this.last = new node(value);
        if (this.isempty())
            this.first = this.last;
        else 
           this.oldlast.next = this.last;
    }
    this.dequeue = function () {
        var queuvalue = this.first.value;
        this.first = this.first.next;
        return queuvalue;
    }
    this.hasnext = function () {
        return this.first.next != undefined;
    }
    this.isempty = function () {
        return this.first == undefined;
    }

}

如何运行给定的内部函数,如 push pop?
D
DrByrd

这是队列的链表版本,它还包括最后一个节点,正如@perkins 所建议的那样,这是最合适的。

// QUEUE Object Definition

var Queue = function() {
  this.first = null;
  this.last = null;
  this.size = 0;
};

var Node = function(data) {
  this.data = data;
  this.next = null;
};

Queue.prototype.enqueue = function(data) {
  var node = new Node(data);

  if (!this.first){ // for empty list first and last are the same
    this.first = node;
    this.last = node;
  } else { // otherwise we stick it on the end
    this.last.next=node;
    this.last=node;
  }

  this.size += 1;
  return node;
};

Queue.prototype.dequeue = function() {
  if (!this.first) //check for empty list
    return null;

  temp = this.first; // grab top of list
  if (this.first==this.last) {
    this.last=null;  // when we need to pop the last one
  }
  this.first = this.first.next; // move top of list down
  this.size -= 1;
  return temp;
};

在出队中,您应该返回 temp.data 。因为那是排队的。
S
Samuel Liew

Javascript 中的常规数组结构是堆栈(先进后出),也可以用作队列(先进先出),具体取决于您进行的调用。

检查此链接以了解如何使 Array 像队列一样工作:

Queues


R
Rajesh Kumar
  var x = 10; 
  var y = 11; 
  var Queue = new Array();
  Queue.unshift(x);
  Queue.unshift(y);

  console.log(Queue)
  // Output [11, 10]

  Queue.pop()
  console.log(Queue)
  // Output [11]

S
Stuart Clark

在我看来,内置数组适用于堆栈。如果你想要 TypeScript 中的队列,这里是一个实现

/**
 * A Typescript implementation of a queue.
 */
export default class Queue {

  private queue = [];
  private offset = 0;

  constructor(array = []) {
    // Init the queue using the contents of the array
    for (const item of array) {
      this.enqueue(item);
    }
  }

  /**
   * @returns {number} the length of the queue.
   */
  public getLength(): number {
    return (this.queue.length - this.offset);
  }

  /**
   * @returns {boolean} true if the queue is empty, and false otherwise.
   */
  public isEmpty(): boolean {
    return (this.queue.length === 0);
  }

  /**
   * Enqueues the specified item.
   *
   * @param item - the item to enqueue
   */
  public enqueue(item) {
    this.queue.push(item);
  }

  /**
   *  Dequeues an item and returns it. If the queue is empty, the value
   * {@code null} is returned.
   *
   * @returns {any}
   */
  public dequeue(): any {
    // if the queue is empty, return immediately
    if (this.queue.length === 0) {
      return null;
    }

    // store the item at the front of the queue
    const item = this.queue[this.offset];

    // increment the offset and remove the free space if necessary
    if (++this.offset * 2 >= this.queue.length) {
      this.queue = this.queue.slice(this.offset);
      this.offset = 0;
    }

    // return the dequeued item
    return item;
  };

  /**
   * Returns the item at the front of the queue (without dequeuing it).
   * If the queue is empty then {@code null} is returned.
   *
   * @returns {any}
   */
  public peek(): any {
    return (this.queue.length > 0 ? this.queue[this.offset] : null);
  }

}

这是一个 Jest 测试

it('Queue', () => {
  const queue = new Queue();
  expect(queue.getLength()).toBe(0);
  expect(queue.peek()).toBeNull();
  expect(queue.dequeue()).toBeNull();

  queue.enqueue(1);
  expect(queue.getLength()).toBe(1);
  queue.enqueue(2);
  expect(queue.getLength()).toBe(2);
  queue.enqueue(3);
  expect(queue.getLength()).toBe(3);

  expect(queue.peek()).toBe(1);
  expect(queue.getLength()).toBe(3);
  expect(queue.dequeue()).toBe(1);
  expect(queue.getLength()).toBe(2);

  expect(queue.peek()).toBe(2);
  expect(queue.getLength()).toBe(2);
  expect(queue.dequeue()).toBe(2);
  expect(queue.getLength()).toBe(1);

  expect(queue.peek()).toBe(3);
  expect(queue.getLength()).toBe(1);
  expect(queue.dequeue()).toBe(3);
  expect(queue.getLength()).toBe(0);

  expect(queue.peek()).toBeNull();
  expect(queue.dequeue()).toBeNull();
});

希望有人觉得这很有用,

干杯,

斯图


M
Melvin Roest

单端队列

这是使用地图的队列。由于插入顺序为 guaranteed,因此您可以像数组一样对其进行迭代。除此之外,这个想法与 Queue.js 非常相似。

我做了一些简单的测试,但没有进行广泛的测试。我还添加了一些我认为很好(通过数组构造)或易于实现的功能(例如 last()first())。

它背后的简单版本/直觉如下:

class Queue {
    constructor() {
        this.offset = 0
        this.data = new Map()
    }

    enqueue(item) {
        const current = this.offset + this.length()
        this.data.set(current, item)
    }

    dequeue() {
        if (this.length() > 0) {
            this.data.delete(this.offset)
            this.offset += 1
        }
    }

    first() {
        return this.data.get(this.offset)
    }

    last() {
        return this.data.get(this.offset + this.length() - 1)
    }

    length() {
        return this.data.size
    }
}

简单版本的问题是,当内存索引超过 9 万亿(Number.MAX_SAFE_INTEGER 的值)时,需要重新映射内存。此外,我认为拥有数组构造可能会很好,并且很高兴看到入队和出队的值被返回。可以通过编写以下代码来解释这一点:

class Queue {
    constructor() {
        this.offset = 0
        this.data = new Map()
        if (arguments.length === 1) this._initializeFromArray(arguments[0])
    }

    enqueue(item) {
        const current = this.offset + this.length()
        this.data.set(current, item)
        let result = this.data.get(current)
        this._remapDataIfMaxMemoryViolation(current, Number.MAX_SAFE_INTEGER)
        return result
    }

    dequeue() {
        let result = undefined
        if (this.length() > 0) {
            result = this.data.get(this.offset)
            this.data.delete(this.offset)
            this.offset += 1
        }
        if (this.length() === 0) this.offset = 0
        return result
    }

    first() {
        return this.data.get(this.offset)
    }

    last() {
        return this.data.get(this.offset + this.length() - 1)
    }

    length() {
        return this.data.size
    }

    _remapDataIfMaxMemoryViolation(current, threshhold) {
        if (current+1 === threshhold) {
            const length = this.length()
            this.offset = 0
            for (const [key, value] of this.data) {
                this.data.set(this.offset, value)
                this.data.delete(key, value)
                this.offset += 1
                if (this.offset === length) break
            }       
        }
    }

    _initializeFromArray(array) {
        for (const value of array) {
            this.data.set(this.offset, value)
            this.offset += 1
        }
    }
}

我在 Chrome 开发者控制台中做了一些测试,在完整版上进行了以下调用。

l = console.log // I'm lazy with typing
q = new Queue()
l('enqueue', q.enqueue(1))
l('enqueue', q.enqueue(2))
l('enqueue', q.enqueue(3))
l('enqueue', q.enqueue("hello"))
l('enqueue', q.enqueue("monkey"))
l('show 5 elements: ', q.data)
l('length', q.length())
l('first', q.first())
l('last', q.last())
l('dequeue', q.dequeue())
l('dequeue', q.dequeue())
l('show 3 elements', q.data)
q._remapDataIfMaxMemoryViolation(q.length()+q.offset-1, 5)
l('show 3 remapped elements', q.data)

l(queue = new Queue([3,4,5,6,7,8,9]))
l(queue.data)

g
gui3

很抱歉碰到这个话题,但我浏览了很多答案,并没有看到任何基于对象的队列的实现,它可以执行入队和出队 O(1) 并且不会浪费内存。

Dmitri Pavlutin 在他的博客上有一个不错的入门代码 https://dmitripavlutin.com/javascript-queue/

它只错过了一个 0 长度的检查,这很容易添加。

这个解决方案的最大和唯一的问题是不断增长的索引,如果队列运行很长时间和/或高速运行(我的目的是处理音频 = 高速),它可能会在某一时刻达到某个数量限制。

对此没有完美的解决方案......只要队列为空,简单的方法就是将索引重置为 0。

最后,我添加了一个 refactor 方法,该方法代价高昂地将所有索引移回开头,以在队列永远不会为空的情况下使用。

https://i.stack.imgur.com/6IcxE.png

class QueueObject {
  constructor () {
    this.data = {}
    this.head = 0
    this.tail = 0
    this.length = 0
  }
  enqueue (value) {
    this.data[this.tail++] = value
    this.length++
  }
  dequeue () {
    let value
    if (this.length > 0) {
      this.length--
      value = this.data[this.head]
      delete this.data[this.head++]
    } else {
      this.head = 0
      this.tail = 0
      value = null
    }
    return value
  }
  refactor () {
    if (this.head > 0) {
      for (let i = this.head; i < this.tail; i++) {
        this.data[i - this.head] = this.data[i]
        delete this.data[i]
      }
      this.tail = this.length
      this.head = 0
    }
  }
}

e
echo

创建一对提供这些数据结构的各种方法(push、pop、peek 等)的类。现在实现这些方法。如果您熟悉堆栈/队列背后的概念,这应该非常简单。你可以用一个数组来实现栈,用一个链表来实现一个队列,当然还有其他方法可以实现。 Javascript 会让这件事变得简单,因为它是弱类型的,所以你甚至不必担心泛型类型,如果你在 Java 或 C# 中实现它,你就必须这样做。


H
Hitesh Joshi

这是我的堆栈实现。

function Stack() {
this.dataStore = [];
this.top = 0;
this.push = push;
this.pop = pop;
this.peek = peek;
this.clear = clear;
this.length = length;
}
function push(element) {
this.dataStore[this.top++] = element;
}
function peek() {
return this.dataStore[this.top-1];
}
function pop() {
return this.dataStore[--this.top];
}
function clear() {
this.top = 0;
}
function length() {
return this.top;
}

var s = new Stack();
s.push("David");
s.push("Raymond");
s.push("Bryan");
console.log("length: " + s.length());
console.log(s.peek());

S
Salar

您可以使用 WeakMaps 来实现 ES6 类中的私有属性以及 JavaScript 语言中字符串属性和方法的好处,如下所示:

const _items = new WeakMap();

class Stack {
  constructor() {
    _items.set(this, []);
  }

push(obj) {
  _items.get(this).push(obj);
}

pop() {
  const L = _items.get(this).length;
  if(L===0)
    throw new Error('Stack is empty');
  return _items.get(this).pop();
}

peek() {
  const items = _items.get(this);
  if(items.length === 0)
    throw new Error ('Stack is empty');
  return items[items.length-1];
}

get count() {
  return _items.get(this).length;
}
}

const stack = new Stack();

//now in console:
//stack.push('a')
//stack.push(1)
//stack.count   => 2
//stack.peek()  => 1
//stack.pop()   => 1
//stack.pop()   => "a"
//stack.count   => 0
//stack.pop()   => Error Stack is empty

N
Niroshan Ratnayake

问候,

在 Javascript 中,栈和队列的实现如下:

堆栈:堆栈是根据后进先出 (LIFO) 原则插入和移除的对象的容器。

Push:方法将一个或多个元素添加到数组的末尾,并返回数组的新长度。

Pop:方法从数组中删除最后一个元素并返回该元素。

队列:队列是根据先进先出(FIFO)原则插入和删除的对象(线性集合)的容器。

Unshift:方法将一个或多个元素添加到数组的开头。

Shift:该方法从数组中删除第一个元素。

让栈 = []; stack.push(1);//[1] stack.push(2);//[1,2] stack.push(3);//[1,2,3] console.log('被插入堆栈中的 1,2,3:', ...stack);堆栈.pop(); //[1,2] console.log('Item 3 was removed:', ...stack);堆栈.pop(); //[1] console.log('Item 2 was removed:', ...stack);让队列 = []; queue.push(1);//[1] queue.push(2);//[1,2] queue.push(3);//[1,2,3] console.log('被插入1,2,3 在队列中:', ...queue); queue.shift();// [2,3] console.log('Item 1 was removed:', ...queue); queue.shift();// [3] console.log('Item 2 was removed:', ...queue);


s
somebody

我在实现 BFS 时遇到了这个线程。在想知道为什么性能如此糟糕之后,我做了一些研究。 array.shift() 通常在 O(n) 中运行,这将我的 BFS 运行时间从 O(V+E) 增加到 O(V^2+E)。

我没有从头开始实现队列,而是使用了 npm 包双端队列,它与以前使用的数组方法兼容,并且像一个魅力一样工作。双端队列可以用作堆栈或队列。

    //import package
    import Deque from 'double-ended-queue';

    //create queue
    let queue = new Deque();
    //append
    queue.push(item);
    //dequeue (get first item inserted)
    let firstItem = queue.shift();

    //pop (get last item inserted)
    let lastItem = queue.pop();

L
Luke Hutchison

数组是 Javascript 中的堆栈。只需使用 arr.push(x)y = arr.pop()

这是在 Javascript 中实现队列的最简单方法,该队列对于 enqueue(x)y = dequeue() 的摊销时间均为 O(1)。它使用从插入索引到元素的映射。

function newQueue() {
    const queue = {
        headIdx: 0,
        tailIdx: 0,
        elts: {},
        enqueue: (elt) => queue.elts[queue.tailIdx++] = elt,
        dequeue: () => {
            if (queue.headIdx == queue.tailIdx) {
                throw new Error("Queue is empty");
            }
            return queue.elts[queue.headIdx++];
        },
        size: () => queue.tailIdx - queue.headIdx,
        isEmpty: () => queue.tailIdx == queue.headIdx
    };
    return queue;
}

使用链表实现的队列比基于 map 的方法效率更高,使用循环缓冲区实现的队列比基于 map 的方法效率更高,但这两种数据结构的实现更复杂(尤其是循环缓冲区数据结构)。


Y
Yuki-Dreamer

使用两个堆栈构造一个队列。

O(1) 对于入队和出队操作。

class Queue {
  constructor() {
    this.s1 = []; // in
    this.s2 = []; // out
  }

  enqueue(val) {
    this.s1.push(val);
  }

  dequeue() {
    if (this.s2.length === 0) {
      this._move();
    }

    return this.s2.pop(); // return undefined if empty
  }

  _move() {
    while (this.s1.length) {
      this.s2.push(this.s1.pop());
    }
  }
}

此实现的出队时间为 O(n)。如果您将 5 个项目排队,然后将 1 出队,则 while 循环将需要运行所有 5 个项目,将它们推入 s2。
O(1) 测量是针对每个元素的平均值。因为堆栈 1 和 2 的每个元素只会进出一次。
我总是被教导说大 O 是针对这里描述的最坏情况的情况 medium.com/omarelgabrys-blog/… 。这是一个假设,项目将以与排队相同的速率出队。这取决于实施场景。在不知道实施方案的情况下,我认为您不能做出这个假设恕我直言。
是的你是对的。对于这个特定的操作,时间复杂度是 O(n)。但是让我们把它放到一个真实的工程环境中。使用 Queue 的原因或使用价值是当您对该数据结构进行多次 in&out 操作时,例如做 BFS 等。在这种情况下,测量平均性能更有意义。如果你想实现一个确定的 O(1) 解决方案,使用 LinkedList 是不错的选择。