[英]How do you implement a Stack and a Queue in JavaScript?
在 JavaScript 中實現堆棧和隊列的最佳方法是什么?
我正在尋找調車場算法,我將需要這些數據結構。
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
Javascript 有 push 和 pop 方法,它們對普通的 Javascript 數組對象進行操作。
對於隊列,請看這里:
http://safalra.com/web-design/javascript/queues/
隊列可以在 JavaScript 中使用數組對象的 push 和 shift 方法或 unshift 和 pop 方法實現。 雖然這是實現隊列的簡單方法,但對於大隊列來說效率非常低——因為這些方法對數組進行操作,每次調用時,shift 和 unshift 方法都會移動數組中的每個元素。
Queue.js 是一個簡單而高效的 JavaScript 隊列實現,它的出隊函數在分攤常數時間內運行。 因此,對於較大的隊列,它可以比使用數組快得多。
數組。
堆:
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();
如果你想創建自己的數據結構,你可以構建自己的:
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;
};
這是我使用鏈表實現的堆棧和隊列:
// Linked List function Node(data) { this.data = data; this.next = null; } // Stack implemented using LinkedList function Stack() { this.top = null; } Stack.prototype.push = function(data) { var newNode = new Node(data); newNode.next = this.top; //Special attention this.top = newNode; } Stack.prototype.pop = function() { if (this.top !== null) { var topItem = this.top.data; this.top = this.top.next; return topItem; } return null; } Stack.prototype.print = function() { var curr = this.top; while (curr) { console.log(curr.data); curr = curr.next; } } // var stack = new Stack(); // stack.push(3); // stack.push(5); // stack.push(7); // stack.print(); // Queue implemented using 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; } return newNode; } Queue.prototype.print = function() { var curr = this.head; while (curr) { console.log(curr.data); curr = curr.next; } } var queue = new Queue(); queue.enqueue(3); queue.enqueue(5); queue.enqueue(7); queue.print(); queue.dequeue(); queue.dequeue(); queue.print();
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
正如您所看到的,大型數據集的速度要快得多
您可以根據這個概念使用自己的自定義類,這里是您可以用來做這些事情的代碼片段
/*
* 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();
如其他答案中所述,堆棧實現是微不足道的。
但是,我在這個線程中沒有找到任何令人滿意的答案來在 javascript 中實現隊列,所以我自己做了一個。
此線程中有三種類型的解決方案:
array.shift()
效率非常低。延遲移位數組是我心目中最滿意的解決方案,但它們仍然將所有內容存儲在一個大的連續數組中,這可能會出現問題,並且在對數組進行切片時應用程序會錯開。
我使用小數組的鏈表(每個最多 1000 個元素)做了一個實現。 數組的行為類似於延遲移位數組,除了它們從不切片:當數組中的每個元素都被刪除時,數組會被簡單地丟棄。
該軟件包位於具有基本 FIFO 功能的npm上,我最近才推送它。 代碼分為兩部分。
這是第一部分
/** 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 代碼。
有很多方法可以在 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/
/*------------------------------------------------------------------
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))
或者你可以使用兩個數組來實現隊列數據結構。
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
這是一個相當簡單的隊列實現,有兩個目標:
堆棧實現僅共享第二個目標。
// 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; };
如果您通過 push() 和 pop() 函數理解堆棧,那么 queue 只是在相反的意義上進行這些操作之一。 push() 的對立是 unshift(),pop() 的對立是 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
如果您正在尋找具有一些基本操作(基於鏈表)的堆棧和隊列數據結構的 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);
}
}
上面示例中用於堆棧和隊列的 LinkedList 實現可以在此處的 GitHub 上找到。
沒有數組
//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;
}
}
這是隊列的鏈表版本,其中還包括最后一個節點,正如@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;
};
回答有點晚,但我認為這個答案應該在這里。 這是使用稀疏數組冪的 O(1) 入enqueue
和 O(1) dequeue
的隊列實現。
JS 中的稀疏數組大多被忽視,但它們實際上是一個寶石,我們應該在一些關鍵任務中使用它們的力量。
所以這里是一個骨架Queue
實現,它擴展了Array
類型並一直在 O(1) 中完成它的事情。
class Queue extends Array { constructor(){ super() Object.defineProperty(this,"head",{ value : 0 , writable: true }); } enqueue(x) { this.push(x); return this; } dequeue() { var first; return this.head < this.length ? ( first = this[this.head] , delete this[this.head++] , first ) : void 0; // perfect undefined } peek() { return this[this.head]; } } var q = new Queue(); console.log(q.dequeue()); // doesn't break console.log(q.enqueue(10)); // add 10 console.log(q.enqueue("DIO")); // add "DIO" (Last In Line cCc RJDIO reis cCc) console.log(q); // display q console.log(q.dequeue()); // lets get the first one in the line console.log(q.dequeue()); // lets get DIO out from the line
.as-console-wrapper { max-height: 100% !important; }
那么我們這里有潛在的內存泄漏嗎? 不,我不這么認為。 JS 稀疏數組是不連續的。 因此,刪除的項目不應成為陣列內存占用的一部分。 讓 GC 為您完成它的工作。 它是免費的。
一個潛在的問題是, length
屬性會隨着您不斷將項目排入隊列而無限增長。 然而,一旦長度達到某個值,仍然可以實現自動刷新(壓縮)機制以啟動。
編輯:
上面的代碼很好,但delete
操作符雖然仍然是 O(1),但速度很慢。 除了現代 JS 引擎如此優化,對於 < ~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;
}
};
這是一個非常快的隊列結構,並使用私有類字段來隱藏關鍵變量不被窺探。
正如許多人所說:使用push
和pop
的本機數組對於堆棧來說很好,但是使用shift
從隊列中取出元素意味着剩余的元素需要移動,這可能會使其變慢。 在kevinyu 的回答中使用兩個堆棧來制作隊列的想法是修復它的好主意,當然也可以使用 native-array-stacks 來完成。 (編輯:實際上Yuki-Dreamer 已經給出了一個答案,盡管不夠緊湊。我直到現在才注意到它,因為它被不公平地否決了。)
這是一個使用一些 ES5/ES6 功能的緊湊實現,它使隊列 object 的行為盡可能接近 native- push
/ shift
變體,除了每個操作需要分攤的 O(1) 時間:
const queue = () => {
const a = [], b = [];
return {
push: (...elts) => a.push(...elts),
shift: () => {
if (b.length === 0) {
while (a.length > 0) { b.push(a.pop()) }
}
return b.pop();
},
get length() { return a.length + b.length }
}
}
現在你可以這樣做:
const q = queue();
q.push(8);
q.push(9);
q.push(10);
console.log(q.length); // outputs 3
console.log(q.shift()); // outputs 8
q.push(11);
console.log(q.shift()); // outputs 9
console.log(q.shift()); // outputs 10
console.log(q.shift()); // outputs 11
console.log(q.shift()); // outputs undefined
queue
實現對length
使用getter語法,使其看起來像一個屬性,對推送使用rest 參數語法以允許一次推送多個內容。 如果你不想這樣,你可以將第 4 行替換為push: elt => a.push(elt),
。 (但是請注意,您不能將其替換為push: a.push,
就像我自己首先嘗試的那樣,結果非常奇怪:那是因為它導致調用本機 push 方法並將this
設置為隊列 object。)
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]
在我看來,內置數組適合堆棧。 如果你想要一個 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();
});
希望有人覺得這有用,
干杯,
斯圖
我喜歡認為實現堆棧和隊列的最干凈的方法應該是使用一個允許從兩端添加和刪除的容器,然后限制其一端的功能,這可以通過 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();
創建一對類,提供這些數據結構中的每一個都具有的各種方法(push、pop、peek 等)。 現在實現這些方法。 如果您熟悉堆棧/隊列背后的概念,這應該非常簡單。 您可以使用數組實現堆棧,並使用鏈表實現隊列,當然還有其他方法可以實現。 Javascript 將使這變得容易,因為它是弱類型的,因此您甚至不必擔心泛型類型,如果您在 Java 或 C# 中實現它,則必須這樣做。
這是我的堆棧實現。
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());
問候,
在 Javascript 中,棧和隊列的實現如下:
堆棧:堆棧是根據后進先出 (LIFO) 原則插入和刪除對象的容器。
隊列:隊列是根據先進先出 (FIFO) 原則插入和刪除的對象(線性集合)的容器。
Unshift:方法將一個或多個元素添加到數組的開頭。
Shift:該方法從數組中刪除第一個元素。
let stack = []; stack.push(1);//[1] stack.push(2);//[1,2] stack.push(3);//[1,2,3] console.log('It was inserted 1,2,3 in stack:', ...stack); stack.pop(); //[1,2] console.log('Item 3 was removed:', ...stack); stack.pop(); //[1] console.log('Item 2 was removed:', ...stack); let queue = []; queue.push(1);//[1] queue.push(2);//[1,2] queue.push(3);//[1,2,3] console.log('It was inserted 1,2,3 in queue:', ...queue); queue.shift();// [2,3] console.log('Item 1 was removed:', ...queue); queue.shift();// [3] console.log('Item 2 was removed:', ...queue);
您可以使用 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
使用兩個堆棧構造一個隊列。
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());
}
}
}
單端隊列
這是一個使用地圖的隊列。 由於插入順序是有保證的,您可以像數組一樣對其進行迭代。 除此之外,這個想法與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)
很抱歉提到這個話題,但我翻閱了許多答案,並沒有看到任何基於對象的隊列的實現,它可以使用 O(1) 執行入隊和出隊,並且不會浪費內存。
Dmitri Pavlutin 在他的博客上有一個很好的入門代碼https://dmitripavlutin.com/javascript-queue/
它只錯過了一個 0 長度檢查,這是微不足道的添加。
這個解決方案的最大也是唯一的問題是不斷增長的索引,如果隊列運行很長時間和/或高速運行(我的意圖是處理音頻=高速) ,它可能會在某一點達到一些數量限制。
對此沒有完美的解決方案......當隊列為空時,簡單的方法是將索引重置為 0。
最后,我添加了一個refactor
方法,該方法代價高昂,將所有索引移回開頭,以在隊列永遠不會為空的情況下使用。
性能無疑更好(數字是以毫秒為單位的時間,用於將 10 000 個數字入隊然后出隊) :
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
}
}
}
我在實現 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();
數組是 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 的方法效率更高,但這兩種數據結構的實現更復雜(尤其是循環緩沖區數據結構)。
Well in case someone else needs it you can use this NPM package https://www.npmjs.com/package/data-structures-typescript , it has a queue and also and stack, it supports both javascript and typescript and it's generic so您可以將其與您自己的值類型一起使用;)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.