Comment implémentez-vous une pile et une file d'attente en JavaScript?

741

Quelle est la meilleure façon d'implémenter une pile et une file d'attente en JavaScript?

Je cherche à faire l'algorithme de triage et je vais avoir besoin de ces structures de données.

KingNestor
la source

Réponses:

1347
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

extrait de " 9 conseils javascript que vous ne connaissez peut-être pas "

Corey Ballou
la source
218
Je conseillerais la prudence dans l'utilisation de queue.shift. IIRC ce n'est pas O (1), mais O (n) et peut être trop lent si la file d'attente devient grande.
MAK
20
Je dirais que cela dépend de l'implémentation javascript. Je ne pense pas que ce soit défini dans la spécification javascript.
Georg Schölly
9
Voir code.stephenmorley.org/javascript/queues pour une implémentation simple qui améliore les performances de la file d'attente.
Gili
15
Pour les problèmes de performances de la file d'attente, consultez une comparaison intéressante de trois types de comportements de pile différents sur jsperf.com/queue-push-unshift-vs-shift-pop - Maintenant, si seulement quelqu'un était assez gentil pour inclure une rév de ce jsperf qui contient le script JS mentionné par @Gili ...
Nenotlep
3
J'ai ressuscité le billet de blog lié dans cette réponse car archive.org n'est pas toujours le plus performant. J'ai mis à jour les liens et les images pour qu'ils fonctionnent, mais je n'ai rien changé d'autre.
Chev
87

Javascript a des méthodes push et pop, qui fonctionnent sur des objets de tableau Javascript ordinaires.

Pour les files d'attente, regardez ici:

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

Les files d'attente peuvent être implémentées en JavaScript à l'aide des méthodes push et shift ou des méthodes unshift et pop de l'objet tableau. Bien que ce soit un moyen simple d'implémenter des files d'attente, il est très inefficace pour les grandes files d'attente - en raison des méthodes qui fonctionnent sur les tableaux, les méthodes shift et unshift déplacent chaque élément du tableau à chaque appel.

Queue.js est une implémentation de file d'attente simple et efficace pour JavaScript dont la fonction de mise en file d'attente s'exécute en temps constant amorti. Par conséquent, pour les files d'attente plus importantes, cela peut être considérablement plus rapide que l'utilisation de tableaux.

Robert Harvey
la source
2
Avec le lien que vous avez partagé, il y avait une fonctionnalité de vérification des résultats de référence et je ne vois pas de gains de performances lors du test avec Google Chrome version 59. Queue.js est incohérent avec sa vitesse mais Chrome était preety cohérent avec sa vitesse.
Shiljo Paulson
J'ai également fait une démonstration avec queue.js, cela, la fonction de file d'attente ne supprime pas vraiment l'élément de la file d'attente, donc je me demande si son supposé être comment cela fonctionne? Si oui, comment pouvez-vous récupérer la nouvelle file d'attente après avoir retiré la file d'attente de l'élément précédent? codepen.io/adamchenwei/pen/VxgNrX?editors=0001
Ezeewei
il semble que la file d'attente dans queue.js nécessite également de la mémoire supplémentaire car elle clone le tableau avec une tranche.
JaTo
De plus, le tableau sous-jacent s'agrandit de plus en plus avec chaque élément ajouté. Même si l'implémentation réduit de temps en temps la taille du tableau, la taille globale augmente.
Philipp Mitterer
73

Tableaux.

Empiler:

var stack = [];

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

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

Queue:

var queue = [];

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

//Take first value from queue
var value = queue.shift();
Jani Hartikainen
la source
1
Array.prototype.pop ne supprime pas la valeur du haut (premier élément) du tableau. Il supprime la valeur du bas (dernier élément) du tableau.
Michael Geller
21
@MichaelGeller Le haut de la pile est le dernier élément du tableau. Les méthodes push et pop de tableau se comportent comme une pile.
mrdommyg
@mrdommyg Array.prototype.pop supprime le dernier élément du tableau (voir developer.mozilla.org/en/docs/Web/JavaScript/Reference/… ). Dernier dans ce contexte signifie l'élément avec l'indice le plus élevé. Un tableau dans JS n'a rien à voir avec une pile. Ce n'est pas une pile simplement parce qu'elle a une méthode pop. Pop signifie simplement "supprimer le dernier élément et le renvoyer". Bien sûr, vous pouvez imiter la fonctionnalité d'une pile avec un tableau, mais un tableau n'est toujours pas une pile par définition. C'est toujours une liste (un objet "comme une liste" selon MDN).
Michael Geller
5
@MichaelGeller Le comportement d'une pile est "premier entré, dernier sorti". Si vous l'implémentez à l'aide d'un tableau en JavaScript avec ses méthodes pushet pop, le problème est résolu. Je ne vois pas vraiment votre point ici.
Rax Weber
2
@MichaelGeller Une pile est conceptuelle. Un tableau JS est (entre autres) par définition une pile grâce à l'implémentation de la sémantique de pile. Ce n'est pas parce qu'il implémente également la sémantique des tableaux que cela change. Vous pouvez utiliser un tableau JS comme une pile prête à l'emploi, et dans ce contexte, ce que vous poussez et éclatez est l'élément "supérieur".
Hans
32

Si vous vouliez créer vos propres structures de données, vous pourriez construire les vôtres:

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

Et pour la file d'attente:

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;
};
user2954463
la source
13
Pour éviter d'avoir à parcourir toute la chose pour l'ajouter à la fin, stockez une référence à la dernière via this.last = node;
Perkins
9
N'implémentez jamais une file d'attente comme celle-ci à moins d'avoir une très bonne raison à cela ... alors que cela peut sembler logiquement correct, les processeurs ne fonctionnent pas selon les abstractions humaines. L'itération sur une infrastructure de données qui a des pointeurs partout entraînera des échecs de cache dans le CPU, contrairement à un tableau séquentiel qui est très efficace. blog.davidecoppola.com/2014/05/… CPU CPU HATE pointeurs avec une passion brûlante - ils sont probablement la cause n ° 1 des échecs de cache et d'avoir à accéder à la mémoire depuis la RAM.
Centril
1
c'est une solution tentante, mais je ne vois pas les Nodes créés supprimés lors du popping / dequeuing ... ne resteront-ils pas juste à monopoliser la mémoire jusqu'à ce que le navigateur plante?
cneuro
5
@cneuro Contrairement à C ++, JavaScript est un langage récupéré. Il a un deletemot - clé, mais cela n'est utile que pour marquer une propriété d'un objet comme étant non présente, ce qui est différent de simplement assigner undefinedà la propriété . JavaScript a également un newopérateur, mais qui est juste utilisé pour définir thisun nouvel objet vide lors de l'appel d'une fonction. En C ++, vous devez associer chaque newavec un delete, mais pas en JavaScript car GC. Pour arrêter d'utiliser la mémoire en JavaScript, arrêtez simplement de référencer l'objet et il sera finalement récupéré.
binki
N'est-il pas également nécessaire de vérifier le débordement d'une pile en définissant une taille de pile maximale?
abeille
16

Mon implémentation Stacket QueueutilisationLinked 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();

Rohit
la source
10

Le décalage de tableau Javascript () est lent, surtout lorsqu'il contient de nombreux éléments. Je connais deux façons d'implémenter la file d'attente avec une complexité O (1) amortie.

La première consiste à utiliser un tampon circulaire et un doublage de table. J'ai déjà implémenté cela. Vous pouvez voir mon code source ici https://github.com/kevyuu/rapid-queue

La deuxième façon consiste à utiliser deux piles. Ceci est le code pour la file d'attente avec deux piles

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

Ceci est une comparaison des performances à l'aide de jsPerf

CircularQueue.shift () vs Array.shift ()

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

Comme vous pouvez le voir, c'est beaucoup plus rapide avec un grand ensemble de données

kevinyu
la source
8

Il existe plusieurs façons d'implémenter des piles et des files d'attente en Javascript. La plupart des réponses ci-dessus sont des implémentations assez superficielles et j'essaierais d'implémenter quelque chose de plus lisible (en utilisant les nouvelles fonctionnalités de syntaxe d'es6) et robuste.

Voici l'implémentation de la pile:

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

Et voici comment vous pouvez utiliser la pile:

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 vous souhaitez voir la description détaillée de cette implémentation et comment elle peut être encore améliorée, vous pouvez lire ici: http://jschap.com/data-structures-in-javascript-stack/

Voici le code pour l'implémentation de la file d'attente dans 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
  }
}

Voici comment vous pouvez utiliser cette implémentation:

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

Pour parcourir le didacticiel complet sur la façon dont ces structures de données ont été mises en œuvre et comment les améliorer, vous pouvez consulter la série «Jouer avec les structures de données en javascript» sur jschap.com. Voici les liens pour les files d'attente - http://jschap.com/playing-data-structures-javascript-queues/

Anish K.
la source
7

Vous pouvez utiliser votre propre classe de personnalisation basée sur le concept, ici l'extrait de code que vous pouvez utiliser pour faire les choses

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

et pour vérifier cela, utilisez votre console et essayez ces lignes une par une.

>> var st = new Stack();

>> st.push("BP");

>> st.push("NK");

>> st.getTop();

>> st.getCount();

>> st.displayAll();

>> st.pop();

>> st.displayAll();

>> st.getTop();

>> st.peek();
jforjs
la source
2
Downvote pour une convention de dénomination: méthode qui commence par un capital supposé être un constructeur.
Pavlo
6
/*------------------------------------------------------------------ 
 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))
Arijit Basu
la source
5

Sinon, vous pouvez utiliser deux tableaux pour implémenter la structure des données de file d'attente.

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

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

Si je pop les éléments maintenant, la sortie sera 3,2,1. Mais nous voulons une structure FIFO afin que vous puissiez faire ce qui suit.

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
Ni3
la source
1
Cela ne fonctionne que si vous n'avez jamais pushaprès la première fois que vouspop
jnnnnn
5

Voici une implémentation de file d'attente assez simple avec deux objectifs:

  • Contrairement à array.shift (), vous savez que cette méthode de mise en file d'attente prend un temps constant (O (1)).
  • Pour améliorer la vitesse, cette approche utilise beaucoup moins d'allocations que l'approche de liste chaînée.

L'implémentation de la pile partage le deuxième objectif uniquement.

// 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; };
snydergd
la source
5

L'implémentation de la pile est triviale comme expliqué dans les autres réponses.

Cependant, je n'ai trouvé aucune réponse satisfaisante dans ce fil pour implémenter une file d'attente en javascript, j'ai donc fait la mienne.

Il existe trois types de solutions dans ce fil:

  • Tableaux - La pire solution, l'utilisation array.shift()sur un grand tableau est très inefficace.
  • Listes liées - C'est O (1) mais l'utilisation d'un objet pour chaque élément est un peu excessive, surtout s'il y en a beaucoup et qu'elles sont petites, comme stocker des nombres.
  • Tableaux à décalage retardé - Il consiste à associer un index au tableau. Lorsqu'un élément est retiré de la file d'attente, l'index avance. Lorsque l'index atteint le milieu du tableau, le tableau est coupé en deux pour supprimer la première moitié.

Les tableaux à décalage retardé sont la solution la plus satisfaisante dans mon esprit, mais ils stockent toujours tout dans un grand tableau contigu qui peut être problématique, et l'application va échouer lorsque le tableau est découpé.

J'ai fait une implémentation en utilisant des listes chaînées de petits tableaux (1000 éléments max chacun). Les tableaux se comportent comme des tableaux à décalage retardé, sauf qu'ils ne sont jamais tranchés: lorsque chaque élément du tableau est supprimé, le tableau est simplement supprimé.

Le forfait est sur npm avec des fonctionnalités FIFO de base, je viens de le pousser récemment. Le code est divisé en deux parties.

Voici la première partie

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

Et voici la Queueclasse principale :

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

Les annotations de type ( : X) peuvent facilement être supprimées pour obtenir le code javascript ES6.

coyotte508
la source
4

Si vous comprenez les piles avec les fonctions push () et pop (), alors la file d'attente consiste simplement à effectuer l'une de ces opérations dans le sens opposé. L'opposé de push () est unshift () et l'opposé de pop () es shift (). Alors:

//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
Javier Giovannini
la source
Un mot d'avertissement pour ceux qui écrivent des logiciels critiques. La .shift()méthode n'est pas une implémentation de file d'attente appropriée. Il s'agit de O (n) plutôt que de O (1) et sera lent pour les grandes files d'attente.
Rudi Kershaw
3

Voici la version de liste liée d'une file d'attente qui inclut également le dernier nœud, comme suggéré par @perkins et comme il est le plus approprié.

// 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;
};
DrByrd
la source
Dans la file d'attente, vous devez renvoyer temp.data à la place. Parce que c'est ce qui était en attente.
not-a-robot
3

Si vous recherchez une implémentation ES6 OOP de la structure de données Stack and Queue avec quelques opérations de base (basées sur des listes liées), cela peut ressembler à ceci:

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

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

Et l'implémentation LinkedList qui est utilisée pour Stack et Queue dans les exemples ci-dessus peut être trouvée sur GitHub ici .

Oleksii Trekhleb
la source
2

Pas de tableau (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;
    }

}
Andriy
la source
Comment puis-je exécuter une fonction interne donnée comme le push pop?
Chandan Kumar
2

La structure régulière de tableau en Javascript est une pile (premier entré, dernier sorti) et peut également être utilisée comme file d'attente (premier entré, premier sorti) en fonction des appels que vous effectuez.

Consultez ce lien pour voir comment faire en sorte qu'un tableau agisse comme une file d'attente:

Files d'attente

Justin Niessner
la source
2

Cordialement,

En Javascript, l'implémentation des piles et des files d'attente est la suivante:

Pile: Une pile est un conteneur d'objets qui sont insérés et retirés selon le principe du dernier entré, premier sorti (LIFO).

  • Push: la méthode ajoute un ou plusieurs éléments à la fin d'un tableau et renvoie la nouvelle longueur du tableau.
  • Pop: la méthode supprime le dernier élément d'un tableau et renvoie cet élément.

File d'attente: une file d'attente est un conteneur d'objets (une collection linéaire) qui sont insérés et retirés selon le principe du premier entré, premier sorti (FIFO).

  • Unshift: la méthode ajoute un ou plusieurs éléments au début d'un tableau.

  • Maj: la méthode supprime le premier élément d'un tableau.

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

Daniel Barrientos
la source
1
  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]
Rajesh Kumar
la source
1

Il me semble que le tableau intégré convient à une pile. Si vous voulez une file d'attente en TypeScript, voici une implémentation

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

}

Et voici un Jesttest pour ça

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

J'espère que quelqu'un trouve cela utile,

À votre santé,

Stu

Stuart Clark
la source
0

Créez une paire de classes qui fournissent les différentes méthodes de chacune de ces structures de données (push, pop, peek, etc.). Maintenant, implémentez les méthodes. Si vous connaissez les concepts de pile / file d'attente, cela devrait être assez simple. Vous pouvez implémenter la pile avec un tableau et une file d'attente avec une liste liée, bien qu'il existe certainement d'autres façons de procéder. Javascript vous facilitera la tâche, car il est faiblement typé, vous n'avez donc même pas à vous soucier des types génériques, ce que vous auriez à faire si vous l'implémentiez en Java ou en C #.

écho
la source
0

Voici mon implémentation de piles.

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());
Hitesh Joshi
la source
0

vous pouvez utiliser WeakMaps pour implémenter la propriété privée dans la classe ES6 et les avantages des propriétés et méthodes String en langage JavaScript comme ci-dessous:

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
Salar
la source
0

Construisez une file d'attente à l'aide de deux piles.

O (1) pour les opérations de mise en file d'attente et de retrait de file d'attente.

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());
    }
  }
}
Yuki-Dreamer
la source