Note

DFS(깊이 우선 탐색)BFS(너비 우선 탐색)를 함께 구현하는 문제입니다.


문제

그래프를 DFS로 탐색한 결과와 BFS로 탐색한 결과를 출력하는 프로그램을 작성하시오. 단, 방문할 수 있는 정점이 여러 개인 경우에는 정점 번호가 작은 것을 먼저 방문하고, 더 이상 방문할 수 있는 점이 없는 경우 종료한다. 정점 번호는 1번부터 N번까지이다.

문제 링크: https://www.acmicpc.net/problem/1260

입력

첫째 줄에 정점의 개수 N(1 ≤ N ≤ 1,000), 간선의 개수 M(1 ≤ M ≤ 10,000), 탐색을 시작할 정점의 번호 V가 주어진다. 다음 M개의 줄에는 간선이 연결하는 두 정점의 번호가 주어진다. 어떤 두 정점 사이에 여러 개의 간선이 있을 수 있다. 입력으로 주어지는 간선은 양방향이다.

출력

첫째 줄에 DFS를 수행한 결과를, 그 다음 줄에는 BFS를 수행한 결과를 출력한다. V부터 방문된 점을 순서대로 출력하면 된다.

예제

예제 입력 1:

4 5 1
1 2
1 3
1 4
2 4
3 4

예제 출력 1:

1 2 4 3
1 2 3 4

예제 입력 2:

5 5 3
5 4
5 2
1 2
3 4
3 1

예제 출력 2:

3 1 2 5 4
3 1 4 2 5

문제 분석

이 문제는 DFSBFS를 모두 구현해야 하는 기본적인 그래프 탐색 문제입니다.

그래프 표현

인접 리스트 (Adjacency List) 방식으로 그래프를 표현합니다:

  • 각 정점에 연결된 정점들의 리스트를 저장
  • 양방향 그래프이므로 양쪽 모두에 간선을 추가

예제 입력 1의 그래프:

1: [2, 3, 4]
2: [1, 4]
3: [1, 4]
4: [1, 2, 3]

핵심 포인트

  1. 정점 번호 정렬: 문제 조건에 따라 “정점 번호가 작은 것을 먼저 방문”하므로 각 정점의 인접 리스트를 정렬해야 합니다.
  2. 양방향 그래프: 간선을 양쪽 정점 모두에 추가해야 합니다.
  3. 방문 배열 초기화: DFS와 BFS를 각각 실행하기 전에 방문 배열을 초기화해야 합니다.

해결 방법

  1. 인접 리스트로 그래프를 구성
  2. 각 정점의 인접 리스트를 정렬 (작은 번호 우선)
  3. DFS로 탐색하여 결과 출력
  4. 방문 배열 초기화
  5. BFS로 탐색하여 결과 출력

코드 구현

제공된 코드

from collections import deque
 
def dfs(graph, v, visited):
    # 현재 노드의 방문 처리
    visited[v] = True
    print(v, end = ' ')
    # 현재 노드와 연결된 부분을 재귀적으로 방문
    for i in graph[v]:
        if visited[i] == False:
            dfs(graph, i, visited)
 
def bfs(graph, start, visited):
    queue = deque([start])
    visited[start] = True
 
    # 큐가 빌때까지 반복
    while queue:
        v = queue.popleft()
        print(v, end = ' ')
        for i in graph[v]:
            if not visited[i]:
                queue.append(i)
                visited[i] = True
 
n, m, v = map(int, input().split())
 
visited: list[bool] = [False] * (n + 1)
graph: list[list[int]] = [[] for _ in range(n + 1)]
 
# 그래프 추가
for _ in range(m):
    start, end = map(int, input().split()) # 1 2
    graph[start].append(end)
    graph[end].append(start)
 
# 정렬. 최소 친구들만 오게
for node in graph:
    node.sort()
 
dfs(graph, v, visited)
print("")
visited = [False] * (n + 1)
bfs(graph, v, visited)

코드 분석

1. 그래프 초기화

n, m, v = map(int, input().split())
visited: list[bool] = [False] * (n + 1)
graph: list[list[int]] = [[] for _ in range(n + 1)]
  • n: 정점의 개수
  • m: 간선의 개수
  • v: 시작 정점
  • visited: 방문 여부를 저장하는 배열 (인덱스 0은 사용하지 않음)
  • graph: 인접 리스트로 그래프를 표현

2. 그래프 구성

for _ in range(m):
    start, end = map(int, input().split())
    graph[start].append(end)
    graph[end].append(start)
  • 양방향 그래프이므로 양쪽 모두에 간선을 추가
  • 예: 1 2 입력 시 graph[1]에 2 추가, graph[2]에 1 추가

3. 정점 번호 정렬

for node in graph:
    node.sort()
  • 중요: 문제 조건에 따라 “정점 번호가 작은 것을 먼저 방문”
  • 각 정점의 인접 리스트를 정렬하여 작은 번호부터 방문하도록 보장

4. DFS 구현

def dfs(graph, v, visited):
    visited[v] = True
    print(v, end = ' ')
    for i in graph[v]:
        if visited[i] == False:
            dfs(graph, i, visited)

자세한 설명은 DFS 알고리즘 포스트를 참고하세요.

5. BFS 구현

def bfs(graph, start, visited):
    queue = deque([start])
    visited[start] = True
 
    while queue:
        v = queue.popleft()
        print(v, end = ' ')
        for i in graph[v]:
            if not visited[i]:
                queue.append(i)
                visited[i] = True

자세한 설명은 BFS 알고리즘 포스트를 참고하세요.

6. 방문 배열 초기화

dfs(graph, v, visited)
print("")
visited = [False] * (n + 1)  # BFS를 위해 초기화
bfs(graph, v, visited)
  • DFS 실행 후 방문 배열을 초기화하여 BFS를 실행
  • 두 탐색이 독립적으로 동작하도록 보장

개선된 코드

더 Pythonic하게 작성한다면:

from collections import deque
 
def dfs(graph, v, visited):
    visited[v] = True
    print(v, end=' ')
    
    for neighbor in graph[v]:
        if not visited[neighbor]:
            dfs(graph, neighbor, visited)
 
def bfs(graph, start, visited):
    queue = deque([start])
    visited[start] = True
    
    while queue:
        v = queue.popleft()
        print(v, end=' ')
        
        for neighbor in graph[v]:
            if not visited[neighbor]:
                visited[neighbor] = True
                queue.append(neighbor)
 
# 메인 코드
n, m, v = map(int, input().split())
graph = [[] for _ in range(n + 1)]
 
for _ in range(m):
    a, b = map(int, input().split())
    graph[a].append(b)
    graph[b].append(a)
 
# 정렬
for node in graph:
    node.sort()
 
# DFS
visited = [False] * (n + 1)
dfs(graph, v, visited)
print()
 
# BFS
visited = [False] * (n + 1)
bfs(graph, v, visited)

시간 및 공간 복잡도

시간 복잡도

  • 그래프 구성: O(M) - 간선 개수만큼
  • 정렬: O(N × log(평균 차수)) - 각 정점의 인접 리스트 정렬
  • DFS: O(N + M) - 모든 정점과 간선을 한 번씩 방문
  • BFS: O(N + M) - 모든 정점과 간선을 한 번씩 방문
  • 전체: O(N + M)

공간 복잡도

  • 그래프 저장: O(N + M) - 인접 리스트
  • 방문 배열: O(N)
  • DFS 스택: O(N) - 최악의 경우 (선형 그래프)
  • BFS 큐: O(N) - 최악의 경우
  • 전체: O(N + M)

예제 동작 과정

예제 입력 1: n=4, m=5, v=1

그래프 구성:

1: [2, 3, 4]
2: [1, 4]
3: [1, 4]
4: [1, 2, 3]

DFS 탐색 (시작: 1):

1 방문 → 출력: 1
  → 2 방문 (1과 연결, 방문 안 함)
     → 출력: 2
     → 4 방문 (2와 연결, 방문 안 함)
        → 출력: 4
        → 3 방문 (4와 연결, 방문 안 함)
           → 출력: 3
           → 더 이상 방문할 정점 없음
결과: 1 2 4 3

BFS 탐색 (시작: 1):

큐: [1]
1 처리 → 출력: 1, 큐에 [2, 3, 4] 추가
큐: [2, 3, 4]
2 처리 → 출력: 2
큐: [3, 4]
3 처리 → 출력: 3
큐: [4]
4 처리 → 출력: 4
큐: []
결과: 1 2 3 4

예제 입력 2: n=5, m=5, v=3

그래프 구성:

1: [2, 3]
2: [1, 5]
3: [1, 4]
4: [3, 5]
5: [2, 4]

DFS 탐색 (시작: 3):

3 → 1 → 2 → 5 → 4
결과: 3 1 2 5 4

BFS 탐색 (시작: 3):

3 → 1, 4 → 2 → 5
결과: 3 1 4 2 5

관련 알고리즘