본 글은 <C++로 쉽게 풀어쓴 자료구조>및 학교 강의를 참고하여 작성되었다.
그래프 표현 방법
그래프는 간단하게 세 종류로 나눌 수 있다. 무방향 그래프, 방향 그래프, 가중치 그래프가 그것이다. 무방향 그래프는 방향이 표시되지 않은 그래프, 즉 양방향으로 갈 수 있는 그래프를 말하고, 방향 그래프는 말그대로 방향이 있는 그래프이다. 마지막으로 가중치 그래프는 두 정점 간의 연결 유무뿐 아니라 연결 강도까지 나타내는 그래프로, 본 글에서 중점적으로 다루게 될 그래프이다. 간단한 예시로 전국 각지의 도시 간 도로를 연결하는 간선을 떠올려볼 수 있다. 이 경우 간선의 가중치는 도로를 이동하는 거리나 비용 등을 나타낼 수 있겠다.
그래프를 표현하는 방법은 크게 두 가지로, 인접행렬 또는 인접리스트를 사용할 수 있지만, 간선마다 다른 가중치를 가져야 하는 그래프라면 인접행렬로 나타내는 것이 직관적이고 보기 좋을 것이다.
이 글에서 살펴볼 것은 아래쪽이다.
코드를 중심으로 살펴보자.
#include <iostream>
#include <fstream>
#include <queue>
#include <list>
#include <vector>
#include <utility>
#include <set>
#include <map>
#include <limits>
using namespace std;
// STL을 사용하지 않을 경우
#define MAX_VTXS 100
#define INF 9999
// 가중치를 고려하지 않는 Graph 클래스이다.
class AdjMatGraph {
protected:
int size; // 정점의 개수, 정점의 이름, 인접행렬(간선)
char vertices[MAX_VTXS];
int adj[MAX_VTXS][MAX_VTXS];
public:
AdjMatGraph() { reset(); }
char getVertex(int i) { return vertices[i]; }
int getEdge(int i, int j) { return adj[i][j]; }
void setEdge(int i, int j, int val) { adj[i][j] = val; }
bool isEmpty() { return size == 0; }
bool isFull() { return size >= MAX_VTXS; }
void reset() { // 그래프를 초기화한다.
size = 0;
for (int i = 0; i < MAX_VTXS; ++i)
for (int j = 0; j < MAX_VTXS; ++j)
setEdge(i, j, 0);
}
void insertVertex(char name) { // 정점 삽입 함수
if (!isFull()) vertices[size++] = name;
else cout << "ERROR : 그래프 정점 개수 초과" << endl;
}
void insertEdge(int u, int v) { // 간선 삽입 함수(가중치를 고려 않으므로 항상 1)
setEdge(u, v, 1);
setEdge(v, u, 1);
}
void display() {
cout << size << endl;
for (int i = 0; i < size; ++i) {
cout << getVertex(i) << " ";
for (int j = 0; j < size; ++j)
cout << getEdge(i, j) << "\t";
cout << endl;
}
}
};
// 위의 클래스를 상속받아 가중치를 고려하는 그래프를 만든다.
// 가중치 그래프만 사용한다면 굳이 두 그래프를 쪼갤 필요는 없지만
// 본 교재에서는 각각의 기능을 하는 객체를 따로 구현하였다.
class WGraph : public AdjMatGraph {
public:
void insertEdge(int u, int v, int weight) { // 가중치 간선 삽입
if (weight > INF) weight = INF;
setEdge(u, v, weight);
}
bool hasEdge(int i, int j) { return (getEdge(i, j) < INF); }
void load(string filename) { // 파일로부터 그래프 정보 불러오기
ifstream fp(filename);
if (fp.is_open()) {
int n, val;
fp >> n;
for (int i = 0; i < n; i++) {
char str[80];
int val;
fp >> str;
insertVertex(str[0]);
for (int j = 0; j < n; j++) {
fp >> val;
insertEdge(i, j, val);
}
}
}
else cout << "File can not be found !" << endl;
fp.close();
}
};
다익스트라 알고리즘
class WGraphDijkstra : public WGraph {
int path[MAX_VTXS];
int dist[MAX_VTXS];
bool found[MAX_VTXS];
public:
int chooseVertex() {
int min = INF;
int minpos = -1;
for (int i = 0; i < size; i++)
if (dist[i] < min && !found[i]) {
min = dist[i];
minpos = i;
}
return minpos;
}
void printDistance() {.
for (int i = 0; i < size; i++) { cout << dist[i] << " "; }
cout << endl;
}
void PrintPath(int start, int end) {
cout << "[최단경로: " << getVertex(start) << "<-" << getVertex(end) << "] " << getVertex(end);
while (path[end] != start) {
cout << "-" << getVertex(path[end]);
end = path[end];
}
cout << "-" << getVertex(path[end]) << endl;
}
void ShortestPath(int s) {
for (int i = 0; i < size; i++) {
dist[i] = getEdge(s, i);
path[i] = s;
found[i] = false;
}
found[s] = true;
dist[s] = 0;
for (int i = 0; i < size; i++) {
cout << "Step" << i + 1 << ": ";
printDistance();
int u = chooseVertex();
found[u] = true;
for (int w = 0; w < size; w++) {
if (found[w] == false)
if (dist[u] + getEdge(u, w) < dist[w]) {
dist[w] = dist[u] + getEdge(u, w);
path[w] = u;
}
}
}
}
};
int main() {
WGraphDijkstra g;
string fn = "graph_sp.txt";
g.load(fn);
cout << "Dijkstra의 최단경로 탐색을 위한 그래프 : " << fn << endl << endl;
g.display();
g.ShortestPath(0);
return 0;
}
이번 숙제는 다음 내용을 포함합니다.
1. 그래프 표현 방법(코드 중심)으로 설명합니다.
2. 다익스트라 알고리즘을 설명하고, 코드를 구현합니다.
3. (옵션) 백준 알고리즘 다음 문제를 풀어봅니다.
- 1753 최단경로
- 1238 파티
- 1504 특정 최단 경로
- 1177 최소비용 구하기
4. (옵션) BFS 관련 문제를 풀어 봅니다.
'C++ > 자료구조' 카테고리의 다른 글
--- (0) | 2022.12.04 |
---|---|
[자료구조] DFS(깊이 우선 탐색)와 BFS(너비 우선 탐색) (0) | 2022.11.06 |
[자료구조] Big-O 표기법과 이진 탐색 트리(binary search tree) (0) | 2022.09.25 |
[자료구조] 힙과 우선순위 큐(heap&priority queue) (2) | 2022.09.21 |
댓글