그래프(Graph)는 객체(데이터)와 그 객체 간의 연결관계를 표현하는 자료구조를 말한다.
단순히 데이터만 저장하는 것이 아니라, 데이터 사이의 관계가 중요할 때 사용하는 비선형 자료구조이다. 그래프에 대해 알아보자.
G = (V, E) (V: 정점의 집합, E: 간선의 집합)

- 한 우물만 끝까지 판다!
- 깊이 우선 탐색으로 vertex(꼭지점)의 자식을 먼저 탐색한다.
// visited: 방문 여부 체크 배열
// graph: 연결 상태를 담은 배열
void dfs(int startNode, boolean[] visited) {
// 1. 체크인: 현재 노드 방문 처리
visited[node] = true;
System.out.println(node + " ");
// 2. 목적지 인가? (필요시 종료 조건 추가)
// 3. 연결된 곳 순회
for (int nextNode : graph[node]) {
// 4. 갈 수 있는가? (방문하지 않았다면?)
if (!visited[nextNode]) {
// 5. 간다! (재귀호출)
dfs(nextNode, visited);
}
}
}
- 주변부터 꼼꼼히 살핀다!
- 너비 우선 탐색으로 vertex(꼭지점)의 형제를 먼저 탐색한다.
void bfs(int startNode, boolean[] visited) {
Queue<Integer> queue = new LinkedList<>();
// 1. 시작점 큐에 넣고 방문 처리
queue.offer(startNode);
visited[startNode] = true;
// 2. 큐가 빌 때까지 반복
while(!queue.isEmpty()) {
// 3. 큐에서 하나 꺼냄 (현재 위치)
int currentNode = queue.poll();
System.out.println(currentNode + " ");
// 4. 현재 위치에서 갈 수 있는 모든 곳을 확인
for (int nextNode : graph[currentNode]) {
// 5. 아직 안 가본 곳이라면?
if (!visited[nextNode]) {
// 6. 큐에 넣고 방문 예약(처리)
queue.offer(nextNode);
visited[nextNode] = true;
}
}
}
}
가장 많이 헷갈리는 3가지 포인트를 정리해 보자.
트리(Tree)와 달리 그래프는 노드끼리 서로 순환(Cycle)할 수 있다. (A → B → A → ...)
만약 visited 체크를 하지 않는다면, 알고리즘은 이미 갔던 곳을 또 방문하며 무한 루프(Infinite Loop)에 빠지게 된다.
즉, visited는 "나 여기 왔다 감! 다시 오지 마!"라고 남기는 발자국이자, 재귀 호출을 멈추게 하는 종료 조건의 핵심이다.
dfs(1) 혹은 bfs(1)을 한 번 호출한다고 해서 무조건 그래프의 모든 노드를 방문하는 것은 아니다. 왜냐하면 그래프는 여러 덩어리(Connected Components)로 분리될 수 있기 때문이다.
즉, 하나의 탐색함수(dfs(), bfs()는 하나의 연결영역만 탐색한다.
int islandCount = 0;
for (int i = 1; i <= N; i++) {
// 아직 방문하지 않은 노드가 있다면? (새로운 섬 발견!)
if (!visited[i]) {
dfs(i); // 그 섬을 싹 다 훑어서 true로 만듦
islandCount++; // 섬 개수 +1
}
}
결국 '다음 방문할 곳을 어디에 적어두느냐'의 차이이다.