Jak zaimplementować stos i kolejkę w JavaScript?

740

Jaki jest najlepszy sposób na implementację stosu i kolejki w JavaScript?

Szukam algorytmu manewrowego i będę potrzebować tych struktur danych.

KingNestor
źródło

Odpowiedzi:

1346
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

zaczerpnięte z „ 9 wskazówek javascript, których możesz nie znać

Corey Ballou
źródło
217
Radziłbym ostrożność przy korzystaniu z queue.shift. IIRC to nie O (1), ale O (n) i może być zbyt wolne, jeśli kolejka się powiększy.
MAK
20
Powiedziałbym, że to zależy od implementacji javascript. Nie sądzę, aby zostało to określone w specyfikacji javascript.
Georg Schölly,
9
Zobacz code.stephenmorley.org/javascript/queues, aby zapoznać się z prostą implementacją, która poprawia wydajność kolejki.
Gili
15
Jeśli chodzi o problemy z wydajnością kolejki, zobacz ładne porównanie trzech różnych rodzajów zachowań stosu na stronie jsperf.com/queue-push-unshift-vs-shift-pop - Teraz, jeśli tylko ktoś był wystarczająco miły, aby zamienić wersję tego jsperf, która by zawierają skrypt JS, o którym wspominał @Gili ...
Nenotlep 24.04.13
3
Wskrzesiłem post na blogu podany w tej odpowiedzi, ponieważ archive.org nie zawsze jest najbardziej wydajne. Zaktualizowałem linki i obrazy, aby działały, ale nie zmieniłem nic innego.
Chev,
87

JavaScript ma metody push i pop, które działają na zwykłych obiektach tablicy JavaScript.

Aby zobaczyć kolejki, spójrz tutaj:

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

Kolejki można zaimplementować w JavaScript za pomocą metod push i shift lub metod unshift i pop obiektu tablicy. Chociaż jest to prosty sposób na implementację kolejek, jest on bardzo nieefektywny w przypadku dużych kolejek - ponieważ metody działają na tablicach, metody shift i unshift przenoszą każdy element w tablicy za każdym razem, gdy są wywoływane.

Queue.js to prosta i wydajna implementacja kolejki dla JavaScript, której funkcja usuwania z pamięci działa w zamortyzowanym stałym czasie. W rezultacie w przypadku większych kolejek może być znacznie szybszy niż korzystanie z tablic.

Robert Harvey
źródło
2
Dzięki udostępnionemu linkowi można było sprawdzić wyniki testu porównawczego i nie widzę wzrostu wydajności podczas testowania z Google Chrome w wersji 59. Queue.js jest niespójny ze swoją prędkością, ale Chrome był precyzyjny zgodny z jego prędkością.
Shiljo Paulson,
Zrobiłem też wersję demo za pomocą pliku queue.js, że funkcja dequeue tak naprawdę nie usuwa elementu z kolejki, więc zastanawiam się, czy to tak działa? Jeśli tak, to jak można odzyskać nową kolejkę po usunięciu z kolejki poprzedniego elementu? codepen.io/adamchenwei/pen/VxgNrX?editors=0001
Ezeewei
wygląda na to, że dequeue w queue.js również wymaga dodatkowej pamięci, ponieważ klonuje tablicę z plasterkiem.
JaTo
Co więcej, podstawowa tablica staje się coraz większa z każdym dodanym elementem. Mimo że implementacja od czasu do czasu zmniejsza rozmiar tablicy, ogólny rozmiar rośnie.
Philipp Mitterer,
73

Tablice

Stos:

var stack = [];

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

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

Kolejka:

var queue = [];

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

//Take first value from queue
var value = queue.shift();
Jani Hartikainen
źródło
1
Array.prototype.pop nie usuwa wartości z górnej (pierwszego elementu) tablicy. Usuwa wartość z dolnej (ostatniego elementu) tablicy.
Michael Geller,
20
@MichaelGeller Górna część stosu jest ostatnim elementem tablicy. Metody push i pop w tablicy zachowują się jak stos.
mrdommyg
@mrdommyg Array.prototype.pop usuwa ostatni element tablicy (patrz developer.mozilla.org/en/docs/Web/JavaScript/Reference/… ). Ostatni w tym kontekście oznacza element o najwyższym indeksie. Tablica w JS nie ma nic wspólnego ze stosem. To nie jest stos tylko dlatego, że ma metodę pop. Pop oznacza po prostu „usuń ostatni element i zwróć go”. Oczywiście można naśladować funkcjonalność stosu za pomocą tablicy, ale tablica nadal nie jest stosem z definicji. Nadal jest to lista (obiekt typu „lista podobna” według MDN).
Michael Geller,
5
@MichaelGeller Zachowanie stosu to „pierwsze weszło, ostatnie wyszło”. Jeśli zaimplementujesz go za pomocą Array w JavaScript z jego metodami pushi pop, wtedy problem zostanie rozwiązany. Naprawdę nie widzę tutaj twojej racji.
Rax Weber
2
@MichaelGeller Stos jest konceptualny. Tablica JS jest (między innymi) z definicji stosem dzięki implementacji semantyki stosu. Tylko dlatego, że implementuje semantykę tablic, nie zmienia tego. Możesz użyć tablicy JS jak stosu po wyjęciu z pudełka, aw tym kontekście to, co naciskasz i pchasz, jest elementem „górnym”.
Hans
32

Jeśli chcesz tworzyć własne struktury danych, możesz zbudować własne:

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

A w kolejce:

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;
};
użytkownik2954463
źródło
13
Aby uniknąć konieczności iterowania całej rzeczy w celu dołączenia do końca, zapisz odniesienie do ostatniego za pośrednictwem this.last = node;
Perkins
9
Nigdy nie implementuj takiej kolejki, chyba że masz naprawdę dobry powód ... chociaż może się to wydawać logicznie poprawne, procesory nie działają zgodnie z ludzkimi abstrakcjami. Iteracja nad strukturą danych, która ma wskaźniki w dowolnym miejscu, spowoduje brak pamięci podręcznej w procesorze, w przeciwieństwie do macierzy sekwencyjnej, która jest bardzo wydajna. blog.davidecoppola.com/2014/05/... Procesory NIENAWIDZĄ wskaźników z płonącą pasją - są one prawdopodobnie pierwszą przyczyną braku pamięci podręcznej i konieczności dostępu do pamięci z pamięci RAM.
Centril
1
jest to kuszące rozwiązanie, ale nie widzę, aby utworzone Nodepliki były usuwane podczas popping / dequeuing ... czy nie będą po prostu siedzieć w pobliżu pamięci, dopóki przeglądarka się nie zawiesi?
cneuro
5
@cneuro W przeciwieństwie do C ++, JavaScript jest językiem wyrzucania elementów bezużytecznych. Ma deletesłowo kluczowe, ale jest to przydatne tylko do oznaczenia właściwości obiektu jako nieobecnej - co różni się od przypisania undefineddo właściwości . JavaScript ma również newoperator, ale służy on tylko do ustawienia thisnowego pustego obiektu podczas wywoływania funkcji. W C ++ musisz sparować każdy newz delete, ale nie w JavaScript, ponieważ GC. Aby przestać używać pamięci w JavaScript, po prostu przestań odwoływać się do obiektu, który ostatecznie zostanie odzyskany.
binki,
Czy nie jest również konieczne sprawdzanie przepełnienia stosu poprzez ustawienie maksymalnego rozmiaru stosu?
pszczoła
16

Moja implementacja Stacki QueueużywanieLinked 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
źródło
10

Przesunięcie tablicy JavaScript () jest wolne, szczególnie przy przechowywaniu wielu elementów. Znam dwa sposoby implementacji kolejki o zamortyzowanej złożoności O (1).

Pierwszym z nich jest użycie okrągłego bufora i podwojenie tabeli. Wcześniej to zaimplementowałem. Możesz zobaczyć mój kod źródłowy tutaj https://github.com/kevyuu/rapid-queue

Drugi sposób polega na użyciu dwóch stosów. To jest kod kolejki z dwoma stosami

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

Jest to porównanie wydajności przy użyciu jsPerf

CircularQueue.shift () vs Array.shift ()

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

Jak widać, przy dużym zestawie danych jest to znacznie szybsze

kevinyu
źródło
8

Istnieje wiele sposobów implementacji stosów i kolejek w JavaScript. Większość powyższych odpowiedzi to dość płytkie implementacje i starałbym się zaimplementować coś bardziej czytelnego (przy użyciu nowych funkcji składni es6) i solidnego.

Oto implementacja stosu:

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

I w ten sposób możesz użyć stosu:

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

Jeśli chcesz zobaczyć szczegółowy opis tej implementacji i sposobów jej dalszej poprawy, możesz przeczytać tutaj: http://jschap.com/data-structures-in-javascript-stack/

Oto kod do implementacji kolejki w 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
  }
}

Oto jak możesz użyć tej implementacji:

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

Aby zapoznać się z pełnym samouczkiem, w jaki sposób zaimplementowano te struktury danych i jak można je dalej ulepszyć, możesz przejrzeć serię „Zabawa ze strukturami danych w javascript” na stronie jschap.com. Oto linki do kolejek - http://jschap.com/playing-data-structures-javascript-queues/

Anish K.
źródło
7

Możesz użyć własnej klasy dostosowywania opartej na koncepcji, tutaj fragment kodu, którego możesz użyć do zrobienia tego

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

i aby to sprawdzić, użyj konsoli i wypróbuj kolejno te linie.

>> 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
źródło
2
Głosuj za konwencją nazewnictwa: metoda rozpoczynająca się od kapitału zakładanego jako konstruktor.
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
źródło
5

Albo możesz użyć dwóch tablic do implementacji struktury danych kolejki.

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

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

Jeśli wstawię teraz elementy, wynik wyniesie 3,2,1. Ale chcemy struktury FIFO, abyś mógł wykonać następujące czynności.

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
źródło
1
Działa to tylko wtedy, gdy nigdy pushpo raz pierwszypop
jnnnnn
5

Oto dość prosta implementacja kolejki z dwoma celami:

  • W przeciwieństwie do array.shift (), wiesz, że ta metoda usuwania kolejki wymaga stałego czasu (O (1)).
  • Aby zwiększyć szybkość, w tym podejściu stosuje się o wiele mniej alokacji niż w przypadku listy połączonej.

Implementacja stosu ma tylko drugi cel.

// 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
źródło
5

Implementacja stosu jest banalna, jak wyjaśniono w innych odpowiedziach.

Jednak w tym wątku nie znalazłem zadowalających odpowiedzi na implementację kolejki w javascript, więc zrobiłem własną.

Istnieją trzy rodzaje rozwiązań w tym wątku:

  • Tablice - najgorsze rozwiązanie w przypadku użycia array.shift()dużej tablicy jest bardzo nieefektywne.
  • Listy połączone - to O (1), ale użycie obiektu dla każdego elementu jest nieco nadmierne, szczególnie jeśli jest ich dużo i są one małe, jak na przykład przechowywanie liczb.
  • Tablice przesunięcia opóźnionego - polega na powiązaniu indeksu z tablicą. Kiedy element jest odbarwiany, indeks przesuwa się do przodu. Gdy indeks osiągnie środek tablicy, tablica jest podzielona na dwie części, aby usunąć pierwszą połowę.

Opóźnione tablice zmiany są moim zdaniem najbardziej zadowalającym rozwiązaniem, ale wciąż przechowują wszystko w jednej dużej ciągłej tablicy, co może być problematyczne, a aplikacja będzie się wahać, gdy tablica zostanie podzielona na plasterki.

Wykonałem implementację, używając połączonych list małych tablic (maks. 1000 elementów każda). Tablice zachowują się jak tablice z opóźnionym przesunięciem, z tym wyjątkiem, że nigdy nie są dzielone na plasterki: gdy każdy element w tablicy jest usuwany, tablica jest po prostu odrzucana.

Pakiet jest na npm z podstawową funkcjonalnością FIFO, niedawno go wypchnąłem. Kod jest podzielony na dwie części.

Oto pierwsza część

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

A oto główna Queueklasa:

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

Wpisz adnotacje ( : X) można łatwo usunąć, aby uzyskać kod javascript ES6.

coyotte508
źródło
4

Jeśli rozumiesz stosy za pomocą funkcji push () i pop (), to kolejka polega na wykonaniu jednej z tych operacji w odwrotnym sensie. Przeciwieństwem push () jest unshift () i przeciwieństwo pop () es shift (). Następnie:

//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
źródło
Słowo ostrzeżenia dla osób piszących oprogramowanie o kluczowym znaczeniu dla wydajności. Ta .shift()metoda nie jest poprawną implementacją kolejki. Jest to O (n) zamiast O (1) i będzie powolny w przypadku dużych kolejek.
Rudi Kershaw
3

Oto wersja kolejki z połączoną listą, która zawiera również ostatni węzeł, zgodnie z sugestią @perkins i najbardziej odpowiednią.

// 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
źródło
W dequeue powinieneś zamiast tego zwrócić temp.data. Ponieważ to było w kolejce.
not-a-robot
3

Jeśli szukasz implementacji ESOP OOP struktury danych stosu i kolejki z kilkoma podstawowymi operacjami (opartymi na listach połączonych), może to wyglądać następująco:

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

I implementacja LinkedList, która jest używana dla stosu i kolejki w powyższych przykładach, można znaleźć na GitHub tutaj .

Oleksii Trekhleb
źródło
2

Brak tablic

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

}
Andrij
źródło
Jak uruchomić daną funkcję wewnętrzną, taką jak push pop?
Chandan Kumar,
2

Zwykła struktura tablic w Javascripcie to stos (pierwsze wejście, ostatnie wyjście) i może być również używane jako kolejka (pierwsze wejście, pierwsze wyjście) w zależności od wykonywanych połączeń.

Sprawdź ten link, aby zobaczyć, jak sprawić, by tablica działała jak kolejka:

Kolejki

Justin Niessner
źródło
2

Pozdrowienia,

W języku JavaScript implementacja stosów i kolejek wygląda następująco:

Stos: Stos to pojemnik z obiektami, które są wstawiane i usuwane zgodnie z zasadą „ostatnie weszło-pierwsze wyszło” (LIFO).

  • Push: Metoda dodaje jeden lub więcej elementów na końcu tablicy i zwraca nową długość tablicy.
  • Pop: Metoda usuwa ostatni element z tablicy i zwraca ten element.

Kolejka: Kolejka to kontener obiektów (kolekcja liniowa), które są wstawiane i usuwane zgodnie z zasadą „pierwsze weszło pierwsze wyszło” (FIFO).

  • Unshift: Metoda dodaje jeden lub więcej elementów na początku tablicy.

  • Shift: metoda usuwa pierwszy element z tablicy.

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
źródło
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
źródło
1

Wydaje mi się, że wbudowana tablica jest odpowiednia dla stosu. Jeśli chcesz mieć kolejkę w TypeScript, tutaj jest implementacja

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

}

A oto Jesttest na to

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

Mam nadzieję, że ktoś uzna to za przydatne,

Twoje zdrowie,

Stu

Stuart Clark
źródło
0

Utwórz parę klas, które zapewniają różne metody, które ma każda z tych struktur danych (push, pop, peek itp.). Teraz zaimplementuj metody. Jeśli znasz pojęcia związane ze stosem / kolejką, powinno to być całkiem proste. Możesz zaimplementować stos za pomocą tablicy i kolejki z połączoną listą, chociaż na pewno istnieją inne sposoby na obejście tego. JavaScript ułatwi to, ponieważ jest słabo wpisany, więc nie musisz się nawet martwić o ogólne typy, które musisz zrobić, jeśli implementujesz go w Javie lub C #.

Echo
źródło
0

Oto moja implementacja stosów.

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
źródło
0

możesz użyć WeakMaps do implementacji własności prywatnej w klasie ES6 oraz korzyści z właściwości i metod String w języku JavaScript, takich jak poniżej:

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
źródło
0

Zbuduj kolejkę, używając dwóch stosów.

O (1) dla operacji kolejkowania i usuwania kolejki.

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
źródło