그래프를 DFS로 탐색한 결과와 BFS로 탐색한 결과를 출력하는 프로그램을 작성하시오. 단, 방문할 수 있는 정점이 여러 개인 경우에는 정점 번호가 작은 것을 먼저 방문하고, 더 이상 방문할 수 있는 점이 없는 경우 종료한다. 정점 번호는 1번부터 N번까지이다.
첫째 줄에 정점의 개수 N(1 ≤ N ≤ 1,000), 간선의 개수 M(1 ≤ M ≤ 10,000), 탐색을 시작할 정점의 번호 V가 주어진다. 다음 M개의 줄에는 간선이 연결하는 두 정점의 번호가 주어진다. 어떤 두 정점 사이에 여러 개의 간선이 있을 수 있다. 입력으로 주어지는 간선은 양방향이다.
첫째 줄에 DFS를 수행한 결과를, 그 다음 줄에는 BFS를 수행한 결과를 출력한다. V부터 방문된 점을 순서대로 출력하면 된다.
4 5 1
1 2
1 3
1 4
2 4
3 4
1 2 4 3
1 2 3 4
5 5 3
5 4
5 2
1 2
3 4
3 1
3 1 2 5 4
3 1 4 2 5
간단하게 DFS, BFS를 파이썬으로 실행 할 수 있는지 요구 하는 문제다.
DFS, BFS를 하려면 먼저 그래프에 대한 이해를 해야한다.

그래프는 G라고 표시한다.
그리고 G는 G(V,E)라고 표시하며 각 요소는 아래와 간다.
양방향의 의미. 즉, (A,B) = (B,A) 순서가 상관 없다. !
한쪽 방향으로만 갈 수 있는 특징. <A,B> ≠ <B,A> 이다.


차수는 하나의 정점 입장에서 다른 정점이 얼마나 연결 되어 있는지를 나타낸다.
위 그림에서 4입장에서는 3하나만 연결 되어 있으니 이때의 차수는 1이라고 한다.
그럼 3입장에서는 1, 4, 5 이렇게 3개가 연결되어 있으니 차수는 3 이다.
💡 모든 차수의 합 = 에지 개수의 2배

이 그래프는 방향이 있기 때문에 차수를
로 구분한다.
예를 들어 그림(a)를 보면 정점 B입장에서
로 나타낼 수 있음.
💡 진입 차수의 합 == 진출 차수의 합 == 그래프 내 모든 에지의 수
파이썬에서 그래프를 표현하는 방법 중 하나는 인접 행렬을 사용하는 것이다. 인접 행렬은 그래프의 정점들간의 연결관계를 행렬로 표현한 것이다. 보면 알겠지만 행렬의 각 칸은 그래프의 한 정점에서 다른 정점으로 가는 경로가 존재하는지 안하는지를 나타낸다.
점 (정점)의 개수를 k 라고 했을 때 k x k의 행렬을 만들자.
각 행은 해당 점의 연결 상태를 의미한다.
이때의 공간 복잡도는 O(n^2)으로 매우 비효율적이다.
3개의 정점 A,B,C가 있다고 치자. 그런 후 아래 처럼 연결되어 있다고 해보자
A - B
|
C
위 그래프의 인접 행렬은 아래와 같다.
A B C
A 0 1 1
B 1 0 0
C 1 0 0
1은 경로가 존재함(가능) 0은 존재 안함(불가능)을 나타낸다.
행은 출발을, 열은 도착을 나타낸다.
(A,B) = A에서 B로 가는 경로의 존재를 나타낸다.
#정점의 개수
n = 3
# 인접 행렬을 0으로 초기화하기
adj_matrix = [[0]*n for _ in range(n)]
# A, B, C 정점은 인덱스 0, 1, 2로 표현
# A와 B가 연결되어 있음
adj_matrix[0][1] = 1
adj_matrix[1][0] = 1
# B와 C가 연결되어 있음.
adj_matrix[0][2] = 1
adj_matrix[2][0] = 1
# 인접 행렬 출력
for row in adj_matrix:
print(' '.join(map(str, row)))
이렇게 하면 된다.
이렇게 하면 두 정점 간의 연결 여부를 O(1)의 시간 복잡도로 빠르게 확인할 수 있는 장점이 있지만 정점의 수가 많을 때는 많은 메모리를 소비한다.
혹은 인접 리스트로 표현하는 방법이 있는데 파이썬은 포인터가 없어서 출발을 키로 도착을 값으로 해서 표현하기도 한다.
깊이 우선 탐색이라고 한다.
동작은 아래와 같이 된다.
너비 우선 탐색이라고 한다.
동작은 아래처럼 이뤄진다.
from collections import deque
N, M, V = map(int, input().split())
graph = [[] * (N + 1) for _ in range(N + 1)]
for _ in range(M):
a, b = map(int, input().split())
graph[a][b] = True
graph[b][a] = True
visited1 = [False] * (N + 1) # dfs의 방문기록
visited2 = [False] * (N + 1) # bfs의 방문기록
def bfs(V):
q = deque([V]) # pop메서드의 시간복잡도가 낮은 덱 내장 메서드를 이용한다
visited2[V] = True # 해당 V 값을 방문처리
while q: # q가 빌때까지 돈다.
V = q.popleft() # 큐에 있는 첫번째 값 꺼낸다.
print(V, end=" ") # 해당 값 출력
for i in range(1, N + 1): # 1부터 N까지 돈다
if not visited2[i] and graph[V][i]: # 만약 해당 i값을 방문하지 않았고 V와 연결이 되어 있다면
q.append(i) # 그 i 값을 추가
visited2[i] = True # i 값을 방문처리
def dfs(V):
visited1[V] = True # 해당 V값 방문처리
print(V, end=" ")
for i in range(1, N + 1):
if not visited1[i] and graph[V][i]: # 만약 i값을 방문하지 않았고 V와 연결이 되어 있다면
dfs(i) # 해당 i 값으로 dfs를 돈다.(더 깊이 탐색)
dfs(V)
print()
bfs(V)