그래프 개념을 이해하고 실제 코드로 구현해보자!!
| 용어 | 설명 |
|---|---|
| 정점(Vertex, Node) | 그래프에서 데이터 하나를 의미하는 점 |
| 간선(Edge) | 정점과 정점을 연결하는 선, 관계를 나타냄 |
| 방향 그래프(Directed Graph) | 간선에 방향이 있는 그래프 |
| 무방향 그래프(Undirected Graph) | 간선에 방향이 없는 그래프 |
| 가중치(Weight) | 간선에 부여된 비용이나 값 |
| 인접(Adjacent) | 두 정점이 간선으로 직접 연결되어 있는 상태 |
| 차수(Degree) | 정점에 연결된 간선의 개수 (방향 그래프는 진입차수/진출차수) |
| 구분 | 인접 행렬 (Adjacency Matrix) | 인접 리스트 (Adjacency List) |
|---|---|---|
| 장점 | - 간선 존재 여부를 O(1) 시간에 즉시 확인 가능 | - 간선 탐색 시 해당 정점에 연결된 정점만 순회하여 빠름 |
| - 구현이 간단 | - 메모리 사용이 효율적 (O(n + m)) | |
| 단점 | - 희소 그래프에서 메모리 비효율적 (O(n²)) | - 간선 존재 여부 확인 시 인접 리스트를 모두 확인해야 해서 느림 |
| - 노드 번호가 크면 배열 크기가 커져 비효율적 | - 인접 행렬보다 구현이 다소 복잡 |
그래프를 탐색하는 방법에는 깊이 우선 탐색(DFS)와 너비 우선 탐색(BFS)가 있다.
| 한 정점에서 출발해 가능한 깊게 내려가며 방문하는 탐색
시작 정점 방문 및 방문 기록 표시
ㄴ 시작 정점을 방문 처리하고, 방문 여부를 기록한다.
시작 정점을 스택에 push
ㄴ 탐색할 정점들을 관리하기 위해 스택에 시작 정점을 넣는다.
스택이 빌 때까지 다음 동작 반복
ㄴ 1) 스택에서 정점 하나를 pop 한다.
ㄴ 2) 그 정점이 아직 방문하지 않은 상태라면 방문 처리하고 출력한다.
ㄴ 3) 그 정점과 연결된 인접 정점들 중 방문하지 않은 정점들을 모두 스택에 push 한다.
모든 연결된 정점이 방문되면 탐색 종료
| 한 정점에서 출발해 인접한 정점부터 차례로 방문하는 탐색
시작 정점 방문 및 방문 기록 표시
ㄴ 시작 정점을 방문 처리하고, 방문 여부를 기록한다.
시작 정점을 큐에 enqueue
ㄴ 탐색할 정점들을 관리하기 위해 큐에 시작 정점을 넣는다.
큐가 빌 때까지 다음 동작 반복
ㄴ 1) 큐에서 정점 하나를 dequeue 한다.
ㄴ 2) 그 정점을 처리(출력 등)한다.
ㄴ 3) 그 정점과 연결된 인접 정점들 중 방문하지 않은 정점들을 모두 방문 처리하고 큐에 enqueue 한다.
모든 연결된 정점이 방문되면 탐색 종료
import java.util.*;
public class GraphSearchMatrix {
static int n = 5; // 정점 개수
static int[][] adjMatrix = new int[n][n]; // 인접 행렬
static boolean[] visited;
// DFS (재귀)
public static void dfs(int node) {
visited[node] = true;
System.out.print(node + " ");
for (int i = 0; i < n; i++) {
// 간선이 존재하고 방문하지 않은 정점이면 재귀 호출
if (adjMatrix[node][i] == 1 && !visited[i]) {
dfs(i);
}
}
}
// BFS (큐)
public static void bfs(int start) {
Queue<Integer> queue = new LinkedList<>();
visited = new boolean[n];
visited[start] = true;
queue.offer(start);
while (!queue.isEmpty()) {
int node = queue.poll();
System.out.print(node + " ");
for (int i = 0; i < n; i++) {
// 간선이 존재하고 방문하지 않은 정점이면 큐에 추가
if (adjMatrix[node][i] == 1 && !visited[i]) {
visited[i] = true;
queue.offer(i);
}
}
}
}
public static void main(String[] args) {
// 인접 행렬 초기화
adjMatrix[0][1] = 1; adjMatrix[1][0] = 1;
adjMatrix[0][2] = 1; adjMatrix[2][0] = 1;
adjMatrix[1][3] = 1; adjMatrix[3][1] = 1;
adjMatrix[1][4] = 1; adjMatrix[4][1] = 1;
visited = new boolean[n];
System.out.print("DFS: ");
dfs(0); // 0번 정점부터 DFS
System.out.println();
System.out.print("BFS: ");
bfs(0); // 0번 정점부터 BFS
}
}
import java.util.*;
public class GraphSearchList {
static int n = 5; // 정점 개수
static List<List<Integer>> adjList = new ArrayList<>();
static boolean[] visited;
// DFS (재귀)
public static void dfs(int node) {
visited[node] = true;
System.out.print(node + " ");
for (int next : adjList.get(node)) {
if (!visited[next]) {
dfs(next);
}
}
}
// BFS (큐)
public static void bfs(int start) {
Queue<Integer> queue = new LinkedList<>();
visited = new boolean[n];
visited[start] = true;
queue.offer(start);
while (!queue.isEmpty()) {
int node = queue.poll();
System.out.print(node + " ");
for (int next : adjList.get(node)) {
if (!visited[next]) {
visited[next] = true;
queue.offer(next);
}
}
}
}
public static void main(String[] args) {
// 인접 리스트 초기화
for (int i = 0; i < n; i++) {
adjList.add(new ArrayList<>());
}
// 간선 추가
addEdge(0, 1);
addEdge(0, 2);
addEdge(1, 3);
addEdge(1, 4);
visited = new boolean[n];
System.out.print("DFS: ");
dfs(0); // 0번 정점부터 DFS
System.out.println();
System.out.print("BFS: ");
bfs(0); // 0번 정점부터 BFS
}
// 무방향 간선 추가 메서드
public static void addEdge(int u, int v) {
adjList.get(u).add(v);
adjList.get(v).add(u);
}
}
void dfs(int start) {
Stack<Integer> stack = new Stack<>();
stack.push(start);
while (!stack.isEmpty()) {
int node = stack.pop();
if (!visited[node]) {
visited[node] = true;
for (int next : adj[node]) {
if (!visited[next]) stack.push(next);
}
}
}
}