
📅 2025-11-04
➡️ 그래프 알고리즘에 대해 새롭게 알게 된 것 또는 헷갈리는 부분 정리
트리
그래프



const graphMatrix = [
[0, 1, 1, 1], // 1 -> 2, 3, 4
[1, 0, 0, 0], // 2 -> 1
[1, 0, 0, 1], // 3 -> 4
[1, 0, 1, 0], // 4 -> X
];
const graphList = {
1: [2, 3, 4],
2: [1],
3: [1, 4],
4: [1, 2],
};

const graph = {
A: ["B"],
B: ["A", "E", "D", "C"],
E: ["B", "D"], // E 다음은 D
D: ["H", "G", "C", "E", "B"], // D에서 H → G → C 순으로
H: ["D"],
G: ["D"],
C: ["F", "D", "B"], // C 에서 F를 먼저
F: ["C"],
};
// DFS (깊이 우선 탐색) 반복문 기반 -> 스택 사용
function dfs(graph, start) {
const visited = new Set(); // 이미 방문한 노드 저장
const stack = [start]; // 탐색할 노드를 저장하는 스택 (LIFO)
while (stack.length) {
const node = stack.pop(); // 스택의 마지막 노드를 꺼냄
if (!visited.has(node)) {
// 아직 방문 하지 않았을 때
console.log(node); // 방문 출력
visited.add(node); // 방문 표시
// 다음 방문할 인접 노드를 추가
for (const neighbor of graph[node].slice().reverse()) {
if (!visited.has(neighbor)) stack.push(neighbor);
}
}
}
}
// DFS 깊이 우선 탐색 (재귀 기반)
function dfsR(graph, node, visited = new Set()) {
visited.add(node);
console.log(node);
for (const neighbor of graph[node]) {
if (!visited.has(neighbor)) dfsR(graph, neighbor, visited);
}
}
graph[node].slice().reverse()를 쓰는 이유Array.prototype.reverse()는 원본 배열을 직접 변경하기 때문에 graph[node].slice()로 복사본을 만들어 주고, 그 복사본에 reverse()를 적용하여 원본 유지
const graph = {
A: ["B"],
B: ["A", "C", "D", "E"],
C: ["B", "D", "F"],
D: ["C", "E", "G", "H"],
E: ["B", "D"],
F: ["C"],
G: ["D"],
H: ["D"],
};
function bfs(graph, start) {
const visited = new Set(); // 방문한 노드 기록
const queue = [start]; // 다음에 방문할 노드를 담는 큐 (FIFO)
while (queue.length) {
const node = queue.shift(); // 큐의 맨 앞 요소를 꺼냄
if (!visited.has(node)) {
// 아직 방문하지 않았다면
console.log(node); // 방문 출력 (탐색 순서 확인용)
visited.add(node); // 방문 표시
// 인접 노드들을 순회
for (const neighbor of graph[node]) {
if (!visited.has(neighbor)) queue.push(neighbor); // 방문 예정 목록(큐)에 추가
}
}
}
}
bfs(graph, "A");
| 항목 | DFS | BFS |
|---|---|---|
| 방식 | 깊이 우선 | 너비 우선 |
| 구조 | 스택(재귀) | 큐 |
| 장점 | 구현이 간단, 백트래킹에 유리 | 최단 경로 탐색에 유리 |
| 단점 | 최단 경로 보장 안됨 | 구현 복잡, 메모리 많이 사용 |
| 실전 예시 | 미로 찾기, 백트래킹 문제 | 게임 맵 탐색, 네트워크 탐색 |
class Node {
constructor(data) {
this.data = data; // 실제 데이터
this.marked = false; // 방문 마킹
this.adjacent = []; // 인접한 노드
}
}
class Graph {
constructor() {
this.nodes = [];
}
add(data) {
const node = new Node(data);
this.nodes.push(node);
return node;
}
addEdge(node1, node2) {
//넣기 전에 인접한 노드
if (!node1.adjacent.includes(node2)) {
node1.adjacent.push(node2); // node1 -> node2
}
if (!node2.adjacent.includes(node1)) {
node2.adjacent.push(node1); // node2 -> node1
}
}
dfs(start) {
const visited = new Set();
const stack = [start];
while (stack.length) {
const node = stack.pop();
if (!visited.has(node)) {
console.log(node.data);
visited.add(node);
for (let neighbor of node.adjacent) {
if (!visited.has(neighbor)) stack.push(neighbor);
}
}
}
}
bfs(start) {
const visited = new Set();
const queue = [start];
while (queue.length) {
const node = queue.shift();
if (!visited.has(node)) {
console.log(node.data);
visited.add(node);
for (let neighbor of node.adjacent) {
if (!visited.has(neighbor)) queue.push(neighbor);
}
}
}
}
}
const graph = new Graph();
const A = graph.add('A');
const B = graph.add('B');
const C = graph.add('C');
const D = graph.add('D');
const E = graph.add('E');
const F = graph.add('F');
const G = graph.add('G');
const H = graph.add('H');
graph.addEdge(A, B);
graph.addEdge(A, C);
graph.addEdge(B, D);
graph.addEdge(B, E);
graph.addEdge(C, F);
graph.addEdge(D, G);
graph.addEdge(E, H);
/*
A
/ \
B C
/ \ \
D E F
| |
G H
*/
console.log(graph);
console.log('DFS 탐색 순서:');
graph.dfs(A);
console.log('BFS 탐색 순서:');
graph.bfs(A);