그래프란 현실세계의 사물이나 개념 간의 연결 관계를 수학적 모델로 단순화하여 표현한 것을 말한다.
그래프는 정점(Vertex)과 그 사이를 잇는 간선(Edge)으로 이루어진다. G = (V, E)
그래프와 관련된 용어
- 정점, 노드 (Vertex, Node) : 데이터가 저장 된다.
- 간선 (Edge)
- 무향 간선 (Undirected Edge) : 방향이 존재하지 않는 간선, 양방향
- 유향 간선 (Directed Edge) : 방향이 존재하는 간선
- 인접 (Adjacent) : (정점 관점) 두 정점 A, B 사이에 간선이 존재한다면 A, B는 인접한다.
- 부속 (Incident) : (간선 관점) 두 정점 A, B 사이에 간선 e가 존재한다면 간선 e는 정점 A, B에 부속한다.
- 차수 (Degree) : 한 정점에 연결된 간선의 수
- (방향 그래프) in-degree : 정점에 들어오는 간선의 수, out-degree : 나가는 간선의 수
- 자기 간선과 다중 간선
- 자기 간선 (Self-loop) : 자신으로 다시 돌아오는 간선
- 다중 간선 (Multiple / Parallel edges) : 두 개 이상의 간선이 똑같은 두 정점에 부속할 때
- 경로 (Path) : 정점 + 간선이 교대로 구성된 sequence
- 단순 경로 (Simple Path) : 같은 정점을 두 번 이상 가지 않는 경로
- 회로 (Cycle) : A 정점에서 출발했을 때 다시 A 정점으로 돌아오는 경로
- 연결됨 (Connected) : 정점 A에서 정점 B로의 경로가 존재할 때 A와 B는 연결되어 있다.
그래프의 종류
- 무향 그래프 (Undirected Graph) : 무방향 간선으로 이루어진 그래프
- 유향 그래프 (Directed Graph) : 방향 간선으로 이루어진 그래프
- 가중치 그래프 (Weighted Graph) : 가중치(비용)를 갖는 간선들로 이루어진 그래프
- 정규 그래프 (Regular Graph) : 모든 정점이 동일한 차수를 가지는 그래프
- 완전 그래프 (Complete Graph) : 모든 정점이 서로 인접해있는 그래프, 완전 그래프는 정규 그래프
- 연결 그래프 (Connected Graph) : 모든 정점이 연결되어 있어서 모든 정점끼리 경로가 존재하는 그래프
- 부분 그래프 : 어떤 그래프의 부분 부분
- 트리 그래프 : 싸이클을 가지지 않는 연결 그래프, 모든 정점에 대해서 경로가 정확히 1개 존재한다.
그래프의 표현
그래프의 표현 방식에는 인접 행렬, 인접 리스트 2가지 방식이 있다.
인접 행렬 (Adjacency Matrix)
- V x V 이차원 배열 A에 정보를 저장한다.
- Vi, Vj를 연결하는 간선이 존재한다면 A[i][j] = 1, 존재하지 않는다면 A[i][j] = 0
- 가중치 그래프의 경우 1 대신 가중치 정보를 저장한다.
인접 리스트 (Adjacent List)
- V 개의 Linked List로 그래프 정보를 저장한다.
- 가중치 그래프의 경우 (정점 정보, 가중치 정보)를 함께 저장한다. (C++ : pair, Java : class)
그래프의 탐색
그래프를 탐색하는 방법에는 깊이우선탐색(DFS)와 너비우선탐색(BFS)가 있다.
그래프를 "탐색한다"는 것은 하나의 정점으로부터 시작하여 차례대로 모든 정점들을 한 번씩 방문하는 것을 말한다.
깊이 우선 탐색(DFS, Depth-First Search)
깊이우선탐색법이란 루트 노드(혹은 다른 임의의 노드)에서 시작해서 다음 분기(branch)로 넘어가기 전에 해당 분기를 완벽하게 탐색하는 방식을 말한다. 최대한 깊이 내려간 뒤, 더이상 깊이 갈 곳이 없을 경우 옆으로 이동하는 것이다.
DFS는 다음과 같은 코드를 통해 구현된다.
void go(int idx){
cout << idx << '\n';
visited[idx] = 1;
for(int there : adj[idx]){
if(visited[there]) continue;
go(there);
}
return;
}
DFS는 경로의 특징을 저장해둬야 하는 문제에서 유리하다.
예를 들어 각 정점에 숫자가 적혀있고 a부터 b까지 가는 경로를 구하는데 경로에 같은 숫자가 있으면 안 된다는 문제와 같이 특징을 저장해둬야 할 때는 DFS를 사용한다. (BFS는 경로의 특징을 가지지 못하기 때문)
너비 우선 탐색(BFS, Breadth-First Search)
너비우선탐색이란 루트 노드(혹은 다른 임의의 노드)에서 인접한 노드를 먼저 탐색하는 방법으로, 시작 정점으로부터 가까운 정점을 먼저 방문하고 떨어져있는 정점을 나중에 방문한다. 즉 최대한 넓게 이동한 다음, 더 이상 갈 수가 없을 때 아래로 이동하는 것이다.
BFS는 아래와 같은 코드를 통해 구현된다.
queue<int> q;
q.push(i);
visited[i] = 1;
while (!q.empty()) {
int x = q.front();
q.pop();
for (int j = 0; j < graph[x].size(); j++) {
int num = graph[x][j];
if (!visited[num]) {
q.push(num);
visited[num] = 1;
cnt++;
}
}
}
BFS는 주로 최단 경로를 찾고 싶을 때 많이 선택되는 방식이다.
예를 들어 지구 상에 존재하는 모든 친구관계를 그래프로 표현한 후 Sam과 Eddie 사이에 존재하는 경로를 찾는 경우,
DFS는 모든 친구 관계를 다 살펴야 할 지 모르지만, BFS는 Sam과 가까운 관계부터 탐색할 수 있기 때문이다.
최종적으로 BFS와 DFS를 비교하면 다음과 같다.
출처
'Computer Science > 알고리즘, 자료구조' 카테고리의 다른 글
탐색 알고리즘 (0) | 2023.04.01 |
---|---|
Hashing과 Hash Table (0) | 2023.04.01 |
시간 복잡도 (0) | 2023.03.31 |
자료구조 - Tree, Binary Tree (0) | 2023.03.31 |
자료구조 - 선형 자료구조 (0) | 2023.03.31 |