Graph(그래프)
그래프는 연결되어 있는 객체간의 관계를 표현할 수 있는 자료구조로 vertex(정점)와 edge(간선)의 집합으로 이루어진다.
그래프 용어
- 수학적으로는 G = (V,E)로 표시한다.
- V(G)는 그래프 G의 vertex들의 집합
- E(G)는 그래프 G의 edge들의 집합
- Vertex는 Node라고 불린다.
- Edge는 link라고 불린다.
- Vertex의 종류에 따라 무방향 그래프(Undirected Graph)와 방향 그래프(Directed Graph)로 구분된다.
- 무방향 그래프 : ‘S—-E’ 화살표가 없는 선으로 이루어진 형태이다.
- 무방향 그래프는 간선이 방향성이 없는 그래프로 양방향으로 갈 수 있다.
- 정점의 차수(Degree)는 그 정점에 인접한 정점의 수를 말한다.
- 방향 그래프 : ‘S—->E’ 화살표가 있는 선으로 이루어진 형태다.
- 방향 그래프는 간선이 방향성이 있는 그래프로 한쪽방향으로만 갈 수 있다.
- 무방향 그래프 : ‘S—-E’ 화살표가 없는 선으로 이루어진 형태이다.
- 간선에 비용이나 가중치가 할당된 그래프는 가중치 그래프(Weighted graph) 또는 네트워크(network)라고 한다.
- 인접정점(adjacent vertex) : 간선에 의해 직접 연결된 정점을 뜻한다.
- 경로 중에서 반복되는 간선이 없는 경우 단순경로(Simple Path)라고 한다
- 시작정점과 종료정점이 동일하다면 이러한 경로를 사이클(cycle)이라고 한다.
- 완전 그래프(Complete Graph) : 그래프 속에 있는 모든 정점이 서로 연결되어 있는 그래프
- 무방향 완전 그래프의 정점의 수를 n이라고 하면 하나의 정점은 n-1개의 다른 정점으로 연결되므로 간선의 수는 n*(n-1)/2가 된다.
그래프의 표현방법
1. 인접행렬 (adjacency matrix) O(n^2): 2차원 배열인 인접행렬 M의 각 원소는 다음 규칙에 의해 할당한다.
- if(edge(i,j)가 그래프에 존재) M[i][j] = 1
- otherwise M[i][j] = 0
- 그래프에서는 자체 간선을 허용하지 않으므로 인접행렬의 대각선 성분은 모두 0으로 표시한다.
- 무방향 그래프의 인접행렬을 대칭행렬이 된다.
- 방향 그래프의 인접행렬은 일반적으로 대칭이 아니다.
- n개의 정점을 가지는 그래프를 인접행렬로 표현하기 위해서는 간선의 수에 무관하게 항상 n^2개의 메모리 공간이 필요하다.
인접행렬 ADT
AdjacencyMatrix.h
1 |
|
AdjacencyMatrix.c
1 | // |
main.c
1 |
|
2. 인접 리스트(adjacency list) O(n+e) : 각각의 정점에 인접한 정점들을 연결리스트로 표시한 것으로 각 연결리스트이 노드들은 인접 정점을 저장한다.
- 연결리스트들은 헤드 포인터를 가지고 있고 이 헤드 포인터들은 하나의 배열로 구성되어 있어서 정점의 번호만 알면 이 번호를 배열의 인덱스로 하여 각 정점의 연결리스트에 쉽게 접근이 가능하다.
인접리스트 ADT
AdjacencyList.h
1 | // |
AdjacencyList.c
1 | // |
그래프 탐색 방법
1. 깊이 우선 탐색 (Depth First Search : DFS)
- 더 나아갈 길이 보이지 않을 때까지 깊이 들어간다.
- 한 방향으로 계속 가다가 더 이상 갈수 없게 되면 다시 가장 가까운 갈림길로 돌아와서 다른 방향으로 탐색을 진행
- 길이 나오지 않을 때까지 그래프의 정점을 타고 깊이 들어가다 더 이상 방문해왔던 정점말고는 다른 이웃을 갖고 있지 않은 정점을 만나면 뒤로 돌아와 다른 경로로 뻗어 있는 정점을 타고 방문을 재개하는 방식
DFS 알고리즘
- 시작 정점을 밟은 후 이 정점을 ‘방문했음’으로 표시
- 이 정점과 이웃하고 있는 정점(인접정점)중에서 아직 방문하지 않은 곳을 선택하고 이를 시작 정점으로 삼아 다시 깊이 우선탐색을 시작(1단계를 다시하는 것)
- 정점에 더 이상 방문하지 않은 인접 정점이 없으면 이전 정점으로 돌아가서 2단계를 수행
- 이전 정점으로 돌아가도 더 이상 방문할 이웃이 없으면 그래프의 모든 정점을 방문했으므로 탐색을 종료
1 | //u 아직방문하지 않은 정점 |
DFS 소스코드
1 | void DFS(Vertex* v) { |
2. 너비 우선 탐색 (Breadth First Search : BFS)
- 시작 정점부터 가까운 정점을 먼저 방문하고 멀리 떨어져 있는 정점을 나중에 방문하는 순회방법
- 너비 우선 탐색을 하기 위해서 방문한 정점들을 차례로 저장하고 꺼낼 수 있는 Queue가 필요
- 정점이 방문될 때마다 큐에 방문한 정점을 삽입하고 더이상 방문할 인접 정점이 없는 경우 큐 앞에서 정점을 꺼내어 그 정점과 인접한 정점들을 차례대로 방문
BFS 알고리즘
- 시작 정점을 ‘방문했음’으로 표시 하고 큐에 삽입
- 큐로부터 정점을 제거(dequeue)하고 제거한 정점이 이웃하고 있는 인접 정점 중 아직 방문하지 않은 곳들을 ‘방문했음’으로 표시하고 큐에 삽입
- 큐가 비게 되면 탐색이 끝난 것이고 큐가 빌때까지 2를 반복
1 | //u 방문하지 않은 정점 |
BFS 소스 코드
1 | void BFS(Vertex* v, LinkedQueue* queue) { |