[알고리즘] 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 파이썬
반응형
'알고리즘' 카테고리의 다른 글
[알고리즘] 이진 탐색 심화(lower bound, upper bound) (3) | 2022.04.21 |
---|---|
[알고리즘] 이진 탐색(Binary Search) (17) | 2022.04.20 |
[알고리즘] 정렬 (5) | 2022.03.28 |
[알고리즘] 구현 (2) | 2022.03.14 |
[알고리즘] 그리디 (0) | 2022.03.11 |
블로그의 정보
코딩무비
코딩무비