그래프는 엔티티와 그들 사이의 연결관계를 표현한다. 정점의 집합과 이들을 연결하는 간선의 집합으로 표현이 가능하며, 여러가지 표현의 방법이 있다.
간선의 개수가 적은 경우 유용. 모든 인덱스를 링크드 리스트형식으로 저장.
한 정점에서 특정 정점으로 가야 할 때 하나씩 살펴봐야 한다는 단점.
배열을 사용한 인접리스트
DFS : 한 방향으로 갈 수 있는 곳을 우선으로 끝까지 탐색. 인접 정점이 더 없는 경우 이전에 지나쳤던 곳으로 돌아와 다른 방향의 정점으로 탐색을 계속 반복.
int graph[MAX_N][MAX_N];
int stack[STACK_SIZE];
int top;
void dfs(int node) {
bool visited[MAX_N] = {false};
top = -1;
stack[++top] = node; // push
while (top != -1) { // is_empty
int curr = stack[top--]; // pop
if (!visited[curr]) {
visited[curr] = true;
for (int next = 0; next < N; ++next) {
if (!visited[next] && graph[curr][next]) {
stack[++top] = next; // push
}
}
}
}
}
BFS : 시작점의 인접한 정점들을 먼저 차례로 방문한다. 그 뒤에 방문했던 정점으로부터 인접한 정점들을 차례로 방문한다.
int queue[QUEUE_SIZE];
int front = -1;
int rear = -1;
void enqueue(int val) {
if (rear >= QUEUE_SIZE - 1) { overflow; }
else {
queue[++rear] = val;
}
}
void dequeue() {
if (front == rear) { 반환할 게 없음 }
else return queue[++front];
}
rear = (rear + 1) % QUEUE_SIZE
front = (front + 1) % QUEUE_SIZE
int graph[MAX_N][MAX_N];
int queue[QUEUE_SIZE];
int front;
int rear;
void bfs(int node) {
bool visited[MAX_N] = {false};
front = rear = -1;
visited[node] = true;
queue[++rear] = node;
while(front != rear) {
int curr = queue[++front];
do_something_with(curr);
for (int next = 0; next < N; ++next) {
if (!visited[next] && graph[curr][next]) {
visited[next] = true; // queue에 집어넣었으면 방문처리하는 것이 가능하다.
queue[++rear] = next;
}
}
}
}
알아둘 점: visited를 설정하는 타이밍이 실제 방문을 했을 때가 아닌, queue에 넣었을 때라는 것을 기억하자. dfs는 스택에 중복된 원소를 넣을 수밖에 없었는데, 그 이유는 훨씬 전에 넣었던 원소가 언제 pop 할지 알 수 없었기 때문. 하지만 bfs는 중복 원소를 넣을 필요 없이 언제 그 원소가 dequeue되는지 알 수 있다.
bfs는 가중치 없는 그래프에서는 경로의 길이를 구할 수 있다는 특징이 있다. 다른 정점을 한 번씩 방문할 때마다 dist 배열의 정점번호에 인접한 인덱스의 값 + 1 해주기만 하면 된다. dist[next] = dist[curr] + 1
이러면 dist는 시작노드로부터 모든 노드까지 이어지는 길이를 갖게 된다.