JavaScriptでスタックとキューをマスターしよう!初心者向けチュートリアル
JavaScriptでスタックとキューを実装する方法
スタックとキューは、コンピュータサイエンスにおいて重要なデータ構造です。それぞれ異なる動作を持ち、様々な場面で利用されています。
このチュートリアルでは、JavaScriptを使って、スタックとキューをどのように実装できるのかを解説します。
スタックは、**後入れ先出し(LIFO)**と呼ばれる動作を持ちます。つまり、最後に追加された要素が最初に取り出される構造です。
イメージとしては、お皿の積み重ねに似ています。一番上に置いたお皿から順番に取り出していくようなものです。
スタックの操作
スタックには、以下の基本的な操作があります。
- push: スタックに要素を追加します。
- pop: スタックから最後の要素を取り出します。
- isEmpty: スタックが空かどうかを確認します。
class Stack {
constructor() {
this.items = [];
}
push(item) {
this.items.push(item);
}
pop() {
if (this.isEmpty()) {
return undefined;
}
return this.items.pop();
}
peek() {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.items.length - 1];
}
isEmpty() {
return this.items.length === 0;
}
}
const stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
console.log(stack.pop()); // 3
console.log(stack.peek()); // 2
console.log(stack.isEmpty()); // false
イメージとしては、行列に似ています。最初に並んだ人が最初に順番に処理されるようなものです。
キューの操作
- enqueue: キューに要素を追加します。
- front: キューの最初の要素を取得します。
class Queue {
constructor() {
this.items = [];
}
enqueue(item) {
this.items.push(item);
}
dequeue() {
if (this.isEmpty()) {
return undefined;
}
return this.items.shift();
}
front() {
if (this.isEmpty()) {
return undefined;
}
return this.items[0];
}
isEmpty() {
return this.items.length === 0;
}
}
const queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
console.log(queue.dequeue()); // 1
console.log(queue.front()); // 2
console.log(queue.isEmpty()); // false
応用例
スタックとキューは、様々な場面で利用されています。以下に、いくつかの例を紹介します。
- ブラウザの履歴: ブラウザの履歴は、スタックを使って実装することができます。新しいページに移動するたびに、現在のページをスタックに追加します。戻るボタンを押すと、スタックから最後のページを取り出して表示します。
- タスク管理: タスク管理システムでは、キューを使ってタスクを管理することができます。新しいタスクが追加されると、キューの末尾に追加されます。タスクを処理する際には、キューの先頭のタスクを取り出して処理します。
- 深さ優先探索: 深さ優先探索と呼ばれるグラフ探索アルゴリズムでは、スタックを使って探索履歴を管理することができます。
スタックとキューは、基本的なデータ構造ですが、様々な場面で利用されています。JavaScriptを使って、これらのデータ構造を自分で実装することで、動作原理を理解し、応用力を身につけることができます。
- [【JavaScript】Arrayオブジェクト(スタック
スタック
class Stack {
constructor() {
this.items = [];
}
push(item) {
this.items.push(item);
}
pop() {
if (this.isEmpty()) {
return undefined;
}
return this.items.pop();
}
peek() {
if (this.isEmpty()) {
return undefined;
}
return this.items[this.items.length - 1];
}
isEmpty() {
return this.items.length === 0;
}
}
const stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
console.log(stack.pop()); // 3
console.log(stack.peek()); // 2
console.log(stack.isEmpty()); // false
キュー
class Queue {
constructor() {
this.items = [];
}
enqueue(item) {
this.items.push(item);
}
dequeue() {
if (this.isEmpty()) {
return undefined;
}
return this.items.shift();
}
front() {
if (this.isEmpty()) {
return undefined;
}
return this.items[0];
}
isEmpty() {
return this.items.length === 0;
}
}
const queue = new Queue();
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
console.log(queue.dequeue()); // 1
console.log(queue.front()); // 2
console.log(queue.isEmpty()); // false
push
とenqueue
は、それぞれスタックとキューに要素を追加するメソッドです。
これらのメソッドを使って、スタックとキューを様々な用途で利用することができます。
このサンプルコードはあくまでも基本的な例です。
実際の利用状況に合わせて、様々な機能を追加することができます。
例えば、以下のような機能を追加することができます。
- スタックとキューの要素の型を制限する
- スタックとキューをシリアル化/デシリアライズする
これらの機能を追加することで、より汎用性の高いスタックとキューを実装することができます。
JavaScriptでスタックとキューを実装するその他の方法
Generator を用いると、スタックとキューの操作をより簡潔に記述することができます。
例:
function* stack() {
let items = [];
yield function push(item) {
items.push(item);
};
yield function pop() {
if (items.length === 0) {
return undefined;
}
return items.pop();
};
yield function peek() {
if (items.length === 0) {
return undefined;
}
return items[items.length - 1];
};
yield function isEmpty() {
return items.length === 0;
};
}
const stackGen = stack();
const stackPush = stackGen.next().value.push;
const stackPop = stackGen.next().value.pop;
const stackPeek = stackGen.next().value.peek;
const stackIsEmpty = stackGen.next().value.isEmpty;
stackPush(1);
stackPush(2);
stackPush(3);
console.log(stackPop()); // 3
console.log(stackPeek()); // 2
console.log(stackIsEmpty()); // false
ES6 シンボルを用いると、より直感的な名前でスタックとキューの操作を定義することができます。
class Stack {
constructor() {
this._items = [];
this[Symbol.iterator] = function* () {
for (let item of this._items) {
yield item;
}
};
}
push(item) {
this._items.push(item);
}
pop() {
if (this._items.length === 0) {
return undefined;
}
return this._items.pop();
}
peek() {
if (this._items.length === 0) {
return undefined;
}
return this._items[this._items.length - 1];
}
isEmpty() {
return this._items.length === 0;
}
}
const stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
console.log(...stack); // [3, 2, 1]
console.log(stack.pop()); // 3
console.log(stack.peek()); // 2
console.log(stack.isEmpty()); // false
第三者ライブラリを用いた実装
Underscore.js や Ramda.js などのライブラリは、スタックとキューを含む様々なデータ構造を効率的に扱うためのユーティリティを提供しています。
const _ = require('underscore');
const stack = _.stack();
stack.push(1);
stack.push(2);
stack.push(3);
console.log(stack.pop()); // 3
console.log(stack.peek()); // 2
console.log(_.isEmpty(stack)); // false
これらの方法は、それぞれ異なる利点と欠点を持っています。
- Generator: 簡潔で分かりやすいコード記述が可能だが、パフォーマンス面で劣る場合がある。
- ES6 シンボル: 直感的な名前で操作を定義できるが、比較的新しい機能であり、古いブラウザではサポートされない可能性がある。
- 第三者ライブラリ: 汎用性が高く、パフォーマンスも良好だが、ライブラリの追加読み込みが必要となる。
状況に応じて最適な方法を選択することが重要です。
javascript data-structures stack