【JavaScript】階層的なデータ構造の探索(ツリー探索)
はじめに
階層的なデータ構造の探索は、ツリーやグラフのようなデータ構造で情報を検索・取得するための基本的な技法です。JavaScriptでは、ツリー構造を扱う際に、深さ優先探索(DFS)や幅優先探索(BFS)が一般的に使用されます。本稿では、これらの探索方法を解説し、具体的なコード例を通して理解を深めます。
ツリー構造の基本
ツリー構造は、親ノードと子ノードからなる階層的なデータ構造です。ツリーの最上位にあるノードは「ルートノード」と呼ばれ、その下に複数の子ノードが存在することができます。各ノードは他のノードとリンク(親子関係)を持ちます。
const tree = {
value: 1,
children: [
{ value: 2, children: [] },
{
value: 3,
children: [
{ value: 4, children: [] },
{ value: 5, children: [] }
]
}
]
};
上記の例では、ツリーのルートノードは「1」で、子ノードとして「2」と「3」を持っています。ノード「3」にはさらに「4」と「5」という子ノードがあります。
深さ優先探索(DFS)
深さ優先探索は、ツリーを探索する際に、まず可能な限り深く子ノードを探索し、行き止まりに達した場合に後戻りして次のノードを探索する方法です。この方法は、再帰的に実装されることが多いです。
function depthFirstSearch(node) {
console.log(node.value); // ノードを処理
node.children.forEach(child => depthFirstSearch(child)); // 子ノードを再帰的に探索
}
depthFirstSearch(tree); // 出力: 1, 2, 3, 4, 5
この例では、深さ優先探索を実行すると、ツリーのノード「1」から順番に「2」「3」「4」「5」が訪問されます。
幅優先探索(BFS)
幅優先探索は、ツリーのレベルごとに順番に探索を行う方法です。つまり、最初にルートノードを訪れ、その後、同じ深さのノードを左から右へと探索します。
function breadthFirstSearch(root) {
const queue = [root]; // 最初にルートノードをキューに入れる
while (queue.length > 0) {
const node = queue.shift(); // キューからノードを取り出す
console.log(node.value); // ノードを処理
queue.push(...node.children); // 子ノードをキューに追加
}
}
breadthFirstSearch(tree); // 出力: 1, 2, 3, 4, 5
幅優先探索では、最初にノード「1」を処理した後、次にノード「2」と「3」を処理し、最後に「4」と「5」を処理します。
再帰を使った探索
再帰を使用したツリー探索は、特に深さ優先探索で有効です。再帰的な関数呼び出しを利用して、親ノードから子ノードへと順次探索していきます。
function recursiveTraversal(node) {
console.log(node.value); // ノードを処理
if (node.children.length > 0) {
node.children.forEach(child => recursiveTraversal(child)); // 子ノードを再帰的に探索
}
}
recursiveTraversal(tree); // 出力: 1, 2, 3, 4, 5
再帰関数を使用することで、コードがシンプルで直感的になります。子ノードが存在する限り再帰的に探索を続けます。
実際の例
以下は、実際のアプリケーションで使用されるツリー探索の例です。
ファイルシステムのツリー探索
ファイルシステムのディレクトリ構造もツリー構造です。以下のように、深さ優先探索を使ってディレクトリ内のファイルを探索できます。
const filesystem = {
name: "root",
children: [
{ name: "folder1", children: [{ name: "file1.txt", children: [] }] },
{ name: "folder2", children: [{ name: "file2.txt", children: [] }] },
{ name: "file3.txt", children: [] }
]
};
function exploreFileSystem(node) {
console.log(node.name); // ディレクトリまたはファイル名を表示
node.children.forEach(child => exploreFileSystem(child)); // 再帰的に探索
}
exploreFileSystem(filesystem); // 出力: root, folder1, file1.txt, folder2, file2.txt, file3.txt
階層的なメニューの表示
階層的なメニューもツリー構造として表現できます。深さ優先探索を使ってメニュー項目を表示する例を紹介します。
const menu = {
label: "Home",
children: [
{ label: "About Us", children: [] },
{ label: "Services", children: [
{ label: "Web Development", children: [] },
{ label: "SEO", children: [] }
]},
{ label: "Contact", children: [] }
]
};
function displayMenu(node) {
console.log(node.label); // メニュー項目を表示
node.children.forEach(child => displayMenu(child)); // 子メニューを再帰的に表示
}
displayMenu(menu); // 出力: Home, About Us, Services, Web Development, SEO, Contact