[알고리즘] BFS & DFS
by 코딩무비반응형
탐색
많은 양의 데이터 중에서 원하는 데이터를 찾는 과정
그래프, 트리 등의 자료구조 안에서 탐색을 하는 문제를 자주 다룸
대표적인 알고리즘 - DFS(깊이우선 탐색),BFS(너비우선 탐색)
그래프, 트리 등의 자료구조 안에서 탐색을 하는 문제를 자주 다룸
대표적인 알고리즘 - DFS(깊이우선 탐색),BFS(너비우선 탐색)
자료구조란?
데이터를 표현하고 관리하고 처리하기 위한 구조
대표적인 자료구조인 스택과 큐는 다음 두 핵심 함수로 구성됨
- 삽입(Push) : 데이터를 삽입한다.
- 삭제(Pop) : 데이터를 삭제한다.
스택(Stack)
FILO, LIFO 구조
- append() : 리스트의 가장 뒤쪽에 데이터 삽입
- pop() : 리스트의 가장 뒤쪽에서 데이터 꺼냄
큐(Queue)
FIFO 구조
collections 모듈에서 제공하는 deque 자료구조 활용
- append() : 데큐의 가장 뒤쪽에 데이터 삽입
- popleft() : 데큐의 가장 앞쪽의 데이터 꺼냄
list() - deque 객체 -> 리스트 객체
재귀 함수
(Recursive Function)
- 자기 자신을 다시 호출하는 함수
- 컴퓨터 내부에서 재귀 함수의 수행은 스택 자료구조 이용
- 마지막에 호출한 함수가 먼저 수행을 끝내야 그 앞의 함수 호출이 종료 되기 때문
- 재귀 함수를 문제 풀이에서 사용할 때는 재귀 함수가 언제 끝날지, 종료 조건을 꼭 명시
ex) 팩토리얼
그래프의 기본구조
- 노드(Node)와 간선(Edge)로 구성
그래프 표현방식 - 인접 행렬(Adjacency Matrix) : 2차원 배열로 그래프의 연결 관계를 표현하는 방식
- 인접 리스트(Adjacency List) : 리스트로 그래프의 연결 관계를 표현하는 방식
인접 행렬
(Adjacency Matrix)
(Adjacency Matrix)
- 2차원 배열에 각 노드가 연결된 형태를 기록하는 방식
- 연결이 되어 있지 않은 노드끼리는 무한(Inf)의 비용이라고 작성
- 모든 관계를 저장하므로 노드 개수가 많을수록 메모리 불필요하게 낭비
- 두 노드가 연결되어 있는지 확인하는 속도가 빠름
INF = 999999 G = [ [0,7,5], [7,0,INF], [5,INF,0] ] print(G)
인접 리스트
(Adjacency List)
(Adjacency List)
- 리스트 자료형 이용
- 모든 노드에 연결된 노드에 대한 정보를 차례대로 연결하여 저장
- 노드 개수가 많을수록 메모리를 효율적으로 사용
- 두 노드가 연결되어 있는지 확인하는 속도가 느림
G = [[] for _ in range(3)] # 노드 0에 연결된 노드 정보 저장(노드, 거리) G[0].append((1,7)) G[0].append((2,5)) # 노드 1에 연결된 노드 정보 저장(노드, 거리) G[1].append((0,7)) # 노드 2에 연결된 노드 정보 저장(노드, 거리) G[2].append((0,5)) print(G)
DFS(Depth-First Search)
DFS(Depth-First Search)
- 깊이 우선 탐색
- 그래프에서 깊은 부분을 우선적으로 탐색하는 알고리즘
- 스택 자료구조 이용
DFS 동작 과정
- 탐색 시작 노드를 스택에 삽입하고 방문 처리
- 스택의 최상단 노드에 방문하지 않은 인접 노드가 있으면 그 인접 노드를 스택에 넣고 방문 처리. 방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드 꺼냄
BFS(Breadth-First Search)
BFS(Breadth First Search)
- 너비 우선 탐색
- 가까운 노드부터 탐색하는 알고리즘
- 큐 자료구조 이용
- deque 라이브러리를 이용하는것이 바람직하다
- 일반적으로 dfs 보다 실행속도가 빠름
BFS 동작 과정
- 탐색 시작 노드를 큐에 삽입하고 방문 처리
- 큐에서 노드를 꺼내 해당 노드의 인접 노드 중에서 방문하지 않은 노드를 모두 큐에 삽입하고 방문 처리
DFS | BFS | |
동작 원리 | 스택 | 큐 |
구현 방법 | 재귀 함수 이용 | 큐 자료구조 이용 |
백준 문제(1260, 2606, 2667, 7576)
더보기
1260번- bfs&dfs
- 기본적인 dfs & bfs
- bfs의 경우 큐 자료구조 이용
- dfs의 경우 스택 자료구조 이용(재귀)
- 정점 방문을 위한 visited 리스트 생성
visited = [False for _ in range(N)]
전체 코드
import sys from collections import deque sys.setrecursionlimit(10**6) input = sys.stdin.readline def dfs(G,visited,vertex): """ dfs는 스택 자료구조(재귀) 이용 """ visited[vertex] = True print(vertex+1, end = " ") for p in G[vertex]: if not visited[p]: dfs(G,visited,p) def bfs(G,visited,vertex): """ bfs는 큐 자료구조 이용 """ Q = deque() Q.append(vertex) visited[vertex] = True while Q: current = Q.popleft() print(current+1,end = " ") for p in G[current]: if not visited[p]: Q.append(p) visited[p] = True N, M, V = map(int,input().split()) G = [[]for _ in range(N)] # 인접 리스트 생성 for _ in range(M): s,e = map(int,input().split()) G[s-1].append(e-1) G[e-1].append(s-1) for row in G: row.sort() visited = [False for _ in range(N)] dfs(G,visited,V-1) print() visited = [False for _ in range(N)] bfs(G,visited,V-1)
2606번 - 바이러스
- 1번 컴퓨터로부터 바이러스 생성
- 해당 컴퓨터와 인접한 컴퓨터에 대하여 방문하지 않았으면 cnt +1
for next in G[current]: if not visited[next]: visited[next] = True cnt+=1 Q.append(next)
- 인접 리스트, 인접 행렬 중 인접 리스트 선택
- 간선이 적을 경우 인접 리스트가 실행속도가 빠름
## 전체 코드 ``` python import sys from collections import deque input = sys.stdin.readline N = int(input()) E = int(input()) G = [[] for _ in range(N)] visited = [False for _ in range(N)] for _ in range(E): s,e = map(int,input().split()) G[s-1].append(e-1) G[e-1].append(s-1) Q = deque() Q.append(0) visited[0] = True cnt = 0 while Q: current = Q.popleft() for next in G[current]: if not visited[next]: visited[next] = True cnt+=1 Q.append(next) print(cnt)
2667번 - 단지번호붙이기
- G 값 ( 0: 집이 없는 곳, 1 : 집이 있는 곳, 2: 방문한 곳)
- G[x][y] 값이 1 이라면 dfs 시작 단지 내 아파트 리스트 추가
apts = []
for i in range(N):
for j in range(N):
if G[i][j]==1:
apts.append(dfs(i,j))
- 방문한 아파트에 경우 2로 할당 ``` python if 0<=x<N and 0<=y<N and G[x][y] == 1: # 방문하지 않았다면 G[x][y] = 2 # 방문 표시
- 인근 아파트 (상,하,좌,우)에 대하여 dfs 진행
cnt+=dfs(x-1,y) cnt+=dfs(x,y-1) cnt+=dfs(x,y+1) cnt+=dfs(x+1,y) return cnt
## 전체 코드 ``` python import sys sys.setrecursionlimit(10**6) def dfs(x,y): cnt = 1 if 0<=x<N and 0<=y<N and G[x][y] == 1: # 방문하지 않았다면 G[x][y] = 2 # 방문 표시 cnt+=dfs(x-1,y) cnt+=dfs(x,y-1) cnt+=dfs(x,y+1) cnt+=dfs(x+1,y) return cnt return 0 N = int(input()) G = [] for _ in range(N): G.append(list(map(int,input()))) apts = [] for i in range(N): for j in range(N): if G[i][j]==1: apts.append(dfs(i,j)) apts.sort() # 단지 내 아파트 수 오름차순 정렬 print(len(apts)) # 단지 수 for apt in apts: print(apt)
7576번 - 토마토
- 인접한 토마토에 대하여 익으므로 bfs을 이용하여 구현
- 맨 처음 익은 토마토를 큐에 추가
for i in range(N): for j in range(M): if G[i][j] == 1: Q.append((i,j))
- 만약 익지 않은 토마토(G = 0)가 있다면 -1 출력
- 익은 토마토들만 있다면 G값중 가장 큰 값-1 출력
# 익지 않은 토마토가 있는 지 , 최대 일 수 확인 for i in range(N): for j in range(M): if G[i][j] == 0: is_zero = True max_days = max(max_days,G[i][j]) if is_zero: print(-1) else: print(max_days-1)
전체 코드
import sys from collections import deque input = sys.stdin.readline M, N = map(int,input().split()) G = [] for _ in range(N): G.append(list(map(int,input().split()))) Q = deque() # 처음 익은 토마토는 큐에 추가 for i in range(N): for j in range(M): if G[i][j] == 1: Q.append((i,j)) mx_list = [-1,1,0,0] my_list = [0,0,1,-1] while Q: x,y = Q.popleft() for mx,my in zip(mx_list,my_list): nx = x + mx ny = y + my if 0<=nx <N and 0<=ny<M and G[nx][ny]== 0 : # 익지 않았다면 Q.append((nx,ny)) G[nx][ny] = G[x][y]+1 # days +1 max_days = 0 is_zero = False # 익지 않은 토마토가 있는 지 , 최대 일 수 확인 for i in range(N): for j in range(M): if G[i][j] == 0: is_zero = True max_days = max(max_days,G[i][j]) if is_zero: print(-1) else: print(max_days-1)
출처
이것이 취업을 위한 코딩 테스트다 with 파이썬

블로그의 정보
코딩무비
코딩무비