estructura datos con algoritmos javascript data-structures stack queue

datos - ¿Cómo implementar una pila y una cola en JavaScript?



algoritmos con javascript (21)

Aquí está la versión de la lista enlazada de una cola que también incluye el último nodo, como lo sugiere @perkins y como sea más apropiado.

// 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; };

¿Cuál es la mejor manera de implementar una pila y una cola en JavaScript?

Estoy buscando hacer el algoritmo de derivación y voy a necesitar estas estructuras de datos.


Aquí está mi implementación de pilas.

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());


Aquí hay una implementación de cola bastante simple con dos objetivos:

  • A diferencia de array.shift (), sabes que este método de salida de cola lleva tiempo constante (O (1)).
  • Para mejorar la velocidad, este enfoque utiliza muchas menos asignaciones que el enfoque de lista enlazada.

La implementación de pila comparte el segundo objetivo solamente.

// 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; };


Arrays.

Apilar:

var stack = []; //put value on top of stack stack.push(1); //remove value from top of stack var value = stack.pop();

Cola:

var queue = []; //put value on end of queue queue.push(1); //Take first value from queue var value = queue.shift();


Cree un par de clases que proporcionen los diversos métodos que tiene cada una de estas estructuras de datos (push, pop, peek, etc.). Ahora implementa los métodos. Si está familiarizado con los conceptos detrás de la pila / cola, esto debería ser bastante sencillo. Puede implementar la pila con una matriz y una cola con una lista vinculada, aunque ciertamente hay otras formas de hacerlo. Javascript lo hará fácil, porque está mal escrito, por lo que ni siquiera tiene que preocuparse por los tipos genéricos, lo que tendría que hacer si lo estuviera implementando en Java o C #.


Hay varias formas en las que puedes implementar pilas y colas en Javascript. La mayoría de las respuestas anteriores son implementaciones bastante superficiales y trataría de implementar algo más legible (usando las nuevas características de sintaxis de es6) y robusto.

Aquí está la implementación de la pila:

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; } }

Y así es como puedes usar la pila:

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

Si desea ver la descripción detallada sobre esta implementación y cómo puede mejorarse, puede leer aquí: http://jschap.com/data-structures-in-javascript-stack/

Aquí está el código para la implementación de la cola en 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 } }

Así es como puedes usar esta implementación:

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

Para ver el tutorial completo de cómo se han implementado estas estructuras de datos y cómo se pueden mejorar aún más, es posible que desee leer la serie "Jugar con estructuras de datos en javascript" en jschap.com. Aquí están los enlaces para colas: http://jschap.com/playing-data-structures-javascript-queues/


Javascript array shift () es lento, especialmente cuando se tienen muchos elementos. Conozco dos formas de implementar la cola con complejidad O (1) amortizada.

Lo primero es usar el búfer circular y la duplicación de tablas. He implementado esto antes. Puede ver mi código fuente aquí https://github.com/kevyuu/rapid-queue

La segunda forma es mediante el uso de dos pila. Este es el código para la cola con dos pilas.

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;}

Esta es la comparación de rendimiento usando jsPerf

CircularQueue.shift () vs Array.shift ()

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

Como puede ver, es significativamente más rápido con grandes conjuntos de datos


Javascript tiene métodos push y pop, que operan en objetos ordinarios de matriz de Javascript.

Para colas, mira aquí:

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

Las colas pueden implementarse en JavaScript utilizando los métodos de inserción y desplazamiento o los métodos de cambio y pop del objeto de matriz. Aunque esta es una forma sencilla de implementar colas, es muy ineficiente para colas grandes, ya que los métodos operan en arrays, los métodos shift y unshift mueven cada elemento en el array cada vez que se llaman.

Queue.js es una implementación de cola simple y eficiente para JavaScript cuya función de salida de cola se ejecuta en tiempo constante amortizado. Como resultado, para colas más grandes puede ser significativamente más rápido que usar arreglos.


La estructura regular de Array en Javascript es una pila (primero en entrar, último en salir) y también se puede usar como una cola (primero en entrar, primero en salir) dependiendo de las llamadas que realice.

Mira este enlace para ver cómo hacer que un Array actúe como una Cola:

Queues


La implementación de la pila es trivial como se explica en las otras respuestas.

Sin embargo, no encontré ninguna respuesta satisfactoria en este hilo para implementar una cola en javascript, así que hice las mías.

Hay tres tipos de soluciones en este hilo:

  • Arrays: la peor solución, usar array.shift() en un array grande es muy ineficiente
  • Listas vinculadas: es O (1) pero usar un objeto para cada elemento es un poco excesivo, especialmente si hay muchos de ellos y son pequeños, como almacenar números
  • Arrays de turnos retrasados: consiste en asociar un índice con el array. Cuando un elemento es eliminado, el índice avanza. Cuando el índice llega a la mitad de la matriz, la matriz se divide en dos para eliminar la primera mitad.

Las matrices de turnos retrasados ​​son la solución más satisfactoria en mi mente, pero aún así almacenan todo en una gran matriz contigua que puede ser problemática, y la aplicación se tambaleará cuando la matriz se divida.

Hice una implementación utilizando listas enlazadas de arreglos pequeños (1000 elementos cada uno). Las matrices se comportan como matrices de desplazamiento retardado, excepto que nunca se dividen en segmentos: cuando se elimina cada elemento de la matriz, la matriz simplemente se descarta.

El paquete está en npm con la funcionalidad FIFO básica, acabo de empujarlo recientemente. El código se divide en dos partes.

Aqui esta la primera parte

/** 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; }

Y aquí está la principal clase de 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; }

Las anotaciones de tipo ( : X ) se pueden eliminar fácilmente para obtener el código javascript de ES6.


Me parece que la matriz incorporada está bien para una pila. Si quieres una Cola en TypeScript aquí hay una implementación

/** * 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); } }

Y aquí hay una prueba de Jest para ello.

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(); });

Espero que alguien encuentre esto útil,

Aclamaciones,

Stu


Mi implementación de Stack and Queue usando Linked List

// 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();


No Array (s)

//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; } }


O bien, puede utilizar dos matrices para implementar la estructura de datos de la cola.

var temp_stack = new Array(); var stack = new Array(); temp_stack.push(1); temp_stack.push(2); temp_stack.push(3);

Si hago estallar los elementos ahora, la salida será 3,2,1. Pero queremos estructura FIFO para que pueda hacer lo siguiente.

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


Puedes usar tu propia clase personalizada basada en el concepto, aquí el fragmento de código que puedes usar para hacer las cosas

/* * 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; } } }

y para comprobar esto usa tu consola y prueba estas líneas una por una.

>> var st = new Stack(); >> st.push("BP"); >> st.push("NK"); >> st.getTop(); >> st.getCount(); >> st.displayAll(); >> st.pop(); >> st.displayAll(); >> st.getTop(); >> st.peek();


Si entiendes las pilas con las funciones push () y pop (), la cola es solo para hacer una de estas operaciones en el sentido opuesto. Oposite of push () es unshift () y oposite de pop () es shift (). Entonces:

//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


Si está buscando la implementación ESO OOP de la estructura de datos de la pila y la cola con algunas operaciones básicas (basadas en listas vinculadas), puede tener este aspecto:

Stack.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); } }

Queue.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); } }

Y la implementación de LinkedList que se usa para la pila y la cola en los ejemplos anteriores se puede encontrar en GitHub aquí .


Si quisiera crear sus propias estructuras de datos, podría crear las suyas propias:

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; };

Y para la cola:

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; };


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]


/*------------------------------------------------------------------ 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("") } }, 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 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

tomado de " 9 consejos de javascript que quizás no conozcas "