深さ優先探索(DFS)
グラフを深く探索するアルゴリズム。バックトラッキングで全経路を系統的に調査
O(V+E)
時間計算量
O(V)
空間計算量
中級
難易度
グラフ探索
手法
🌲 実行設定
実装方式
ノード数:6個
辺数:6本
開始ノード:A
ターゲット:なし(全探索)
📊 推奨グラフ
🌲
深さ優先探索を実行してください
左側の設定パネルからグラフを設定し、「DFS実行」ボタンを押してください
💻 実装例(JavaScript)
// 再帰による深さ優先探索
function dfsRecursive(graph, startNode, visited = new Set()) {
// 現在のノードを訪問
visited.add(startNode);
console.log(`訪問: ${startNode}`);
// 隣接ノードを再帰的に探索
const neighbors = graph[startNode] || [];
for (const neighbor of neighbors) {
if (!visited.has(neighbor)) {
dfsRecursive(graph, neighbor, visited);
}
}
}
// スタックによる反復実装
function dfsIterative(graph, startNode) {
const visited = new Set();
const stack = [startNode];
while (stack.length > 0) {
const currentNode = stack.pop();
if (!visited.has(currentNode)) {
visited.add(currentNode);
console.log(`訪問: ${currentNode}`);
// 隣接ノードをスタックに追加(逆順で追加)
const neighbors = graph[currentNode] || [];
for (let i = neighbors.length - 1; i >= 0; i--) {
if (!visited.has(neighbors[i])) {
stack.push(neighbors[i]);
}
}
}
}
}
// 使用例
const graph = {
A: ['B', 'C'],
B: ['D', 'E'],
C: ['F'],
D: [],
E: ['F'],
F: []
};
console.log("=== 再帰実装 ===");
dfsRecursive(graph, 'A');
console.log("=== 反復実装 ===");
dfsIterative(graph, 'A');
// パス探索版(目標ノードまでの経路を発見)
function findPath(graph, start, target, visited = new Set(), path = []) {
visited.add(start);
path.push(start);
if (start === target) {
return path; // 経路を発見
}
const neighbors = graph[start] || [];
for (const neighbor of neighbors) {
if (!visited.has(neighbor)) {
const result = findPath(graph, neighbor, target, visited, [...path]);
if (result) {
return result;
}
}
}
return null; // 経路が見つからない
}
// 連結成分の検出
function getConnectedComponents(graph) {
const visited = new Set();
const components = [];
for (const node in graph) {
if (!visited.has(node)) {
const component = [];
dfsComponent(graph, node, visited, component);
components.push(component);
}
}
return components;
}
function dfsComponent(graph, node, visited, component) {
visited.add(node);
component.push(node);
const neighbors = graph[node] || [];
for (const neighbor of neighbors) {
if (!visited.has(neighbor)) {
dfsComponent(graph, neighbor, visited, component);
}
}
}
🔄 再帰実装 vs 反復実装
再帰実装の特徴
- • 直感的で理解しやすい
- • コードが簡潔で美しい
- • バックトラッキングが自動
- • 関数型プログラミングと相性良
- • 深いグラフでスタックオーバーフローリスク
反復実装の特徴
- • スタックオーバーフロー回避
- • メモリ使用量の制御が容易
- • 大規模グラフでも安全
- • 探索過程の一時停止・再開が可能
- • 明示的なスタック管理が必要
🚀 深さ優先探索の応用分野
基本的な応用
- • 迷路の解法
- • 経路探索
- • 連結性の判定
- • 循環検出
- • 木の走査
アルゴリズム応用
- • トポロジカルソート
- • 強連結成分の検出
- • 橋・関節点の発見
- • 最小全域木(Kruskal)
- • バックトラッキング
実世界の応用
- • ウェブクローリング
- • ファイルシステム探索
- • ゲームAI(ゲーム木探索)
- • SNSの友達関係分析
- • 依存関係解析