如何在JavaScript中实现栈(Stack)和队列(Queue)?
我想要使用逆波兰表达式算法,因此需要这些数据结构。
如何在JavaScript中实现栈(Stack)和队列(Queue)?
我想要使用逆波兰表达式算法,因此需要这些数据结构。
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小技巧"
ArrayList
);这意味着移位很可能涉及到memmove()
;不过你需要检查你选择的JS引擎的源代码来确保。 - Christoph数组。
栈:
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();
push
和pop
方法来实现它,问题就解决了。我不太明白你的观点。 - Rax WeberJavascript有push和pop方法,可以用于普通的Javascript数组对象。
对于队列,请查看这里:
http://safalra.com/web-design/javascript/queues/
可以使用数组对象的push和shift方法,或者使用unshift和pop方法,在JavaScript中实现队列。尽管这是一种简单的实现队列的方式,但对于大型队列非常低效。因为这些方法操作数组,每次调用shift和unshift方法时都会移动数组中的每个元素。
Queue.js是一个简单而高效的JavaScript队列实现,其出队函数以摊销常数时间运行。因此,对于较大的队列,它比使用数组要快得多。
如果你想要创建自己的数据结构,你可以建立自己的:
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;
};
Node
在弹出/出队时被删除...它们不会一直占用内存,直到浏览器崩溃吗? - cneurodelete
关键字,但仅用于将对象的属性标记为不存在,这与仅将属性分配为undefined
是不同的。JavaScript还有一个new
运算符,但只是在调用函数时将this
设置为一个新的空对象。在C++中,您需要将每个new
与一个delete
配对,但在JavaScript中不需要,因为它有GC(垃圾回收)。要停止在JavaScript中使用内存,只需停止引用该对象,它最终将被回收。 - binkiNode
会更慢,而且代码量更大。至于队列,是的,原生数组的shift操作通常是O(n),但是如果没有一个tail来实现队列,则push操作也是O(n),完全违背了编写自己的队列的初衷。同样,只需使用数组来实现队列,直到O(n)的shift开始变得困难,然后使用带有tail
属性的Node
实现进行优化,如此处所示。 - ggorlen这是我使用链表实现栈和队列的代码:
// 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队列实现答案,所以我自己写了一个。
这个帖子中有三种类型的解决方案:
array.shift()
非常低效。在我看来,延迟移位数组是最令人满意的解决方案,但它们仍然将所有内容存储在一个大的连续数组中,这可能会有问题,并且当数组被切割时,应用程序会停顿。
我使用由小型数组(每个数组最多1000个元素)组成的链表进行了实现。这些数组的行为类似于延迟移位数组,除了它们从不被切割:当数组中的每个元素都被删除时,数组被简单地丢弃。
这个包在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代码。
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中实现堆栈(Stacks)和队列(Queues)。上面的大部分答案都是相当肤浅的实现,我将尝试实现更易读且更健壮的内容(使用es6的新语法特性)。
以下是堆栈(Stack)的实现:
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
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/
// 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();