티스토리 뷰
탐색 알고리즘 DFS / BFS
DFS(Depth First Search)
: 깊이 우선 탐색, 그래프에서 깊은 부분을 우선적으로 탐색하는 알고리즘
= 최대한 멀리 있는 노드를 우선으로 탐색
그래프 : 노드(정점)와 간선으로 표현됨!
→ 그래프: 하나의 노드를 시작으로 다수의 노드를 방문하는 것
→ 두 노드가 간선으로 연결되어 있으면 “두 노드는 인접하다”고 표현
프로그래밍에서 그래프는 2가지 방식으로 표현할 수 있음
1. 인접 행렬 : 2차원 배열로 그래프의 연결 관계를 표현하는 방식
→ 파이썬에서는 2차원 리스트로 구현!
INF = 99999999999 # 연결이 되어 있지 않은 노드끼리는 무한의 비용
# 2차원 리스트를 이용해 인접행렬 표현
graph = [
[0,7,5],
[7,0,INF],
[5,INF,0]
]
print(graph)
2. 인접 리스트 : 리스트로 그래프의 연결관계를 표현하는 방식
→ 모든 노드에 연결된 노드에 대한 정보를 차례대로 연결하여 저장!→ 파이썬: 리스트 자료형 append() 이용 , 2차원 리스트 활용!
→ ‘연결 리스트’ 라는 자료구조 이용
# 행이 3개인 2차원 리스트로 인접 리스트 표현
graph = [[] for _ in range(3)]
# 노드 0에 연결된 노드 정보 저장 (노드, 거리)
graph[0].append((1,7))
graph[0].append((2,5))
# 노드 1에 연결된 노드 정보 저장 (노드,거리)
graph[1].append((0,7))
# 노드 2에 연결된 노드 정보 저장 (노드,거리)
graph[2].append((0,5))
print(graph)
인접 행렬 vs 인접 리스트
인접 행렬 | 인접 리스트 |
모든 관계를 저장하므로 노드 개수가 많을수록 메모리 불필요하게 낭비 | 연결된 정보만을 저장하므로 메모리 효율적 |
graph[1][7]만 확인하면 됨 | but, 특정한 두 노드가 연결되어 있는지에 대한 정보를 얻는 속도는 느림 → 연결된 데이터를 하나씩 확인해야 하기 때문 |
특정한 노드와 연결된 모든 인접 노드를 순회해야 하는 경우, 인접 리스트 방식이 메모리 공간의 낭비가 적음 |
→ DFS는 특정한 경로로 검색하다가 특정한 상황에서 최대한 깊숙이 들어가서 노드를 방문한 후, 다시 돌아가 다른 경로를 탐색하는 알고리즘, 스택 자료 구조를 이용 , 데이터 개수가 N개인 경우 O(N) 시간 소요!
- 탐색 시작 노드를 스택에 삽입하고 방문 처리를 하기
- 스택의 최상단 노드에 방문하지 않은 인접 노드가 있으면, 그 노드를 스택에 넣고 방문 처리를 한다. 방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드를 꺼낸다
- 2번의 과정을 더 이상 수행할 수 없을때까지 반복
#DFS 메소드 정의
def dfs(graph, v, visted): #그래프, 노드 정보, 방문정보
# 현재 노드를 방문 처리
visited[v] = True
print(v, end = '')
# 현재 노드와 연결된 다른 노드를 재귀적으로 방문
for i in graph[v]:
if not visited[i]:
dfs(graph, i, visited)
# 각 노드가 연결된 정보를 리스트 자료형으로 표현 (2차원 리스트 - 인접리스트 방식)
graph = [[],
[2,3,8],
[1,7],
[1,4,5],
[3,5],
[3,4],
[7],
[2,6,8],
[1,7]
]
# 각 노드가 방문된 정보를 리스트 자료형으로 표현(1차원 리스트) - 방문여부
visited = [False] * 9
# 정의된 DFS 함수 호출
dfs(graph, 1, visited)
BFS(Breadth First Search)
: 너비 우선 탐색 = 가까운 노드부터 탐색하는 알고리즘
→ 선입선출 방식인 큐 자료구조 이용! (deque 라이브러리 사용), O(N) 시간 소요, DFS보다 수행시간 일반적으로 더 좋다!
- 탐색 시작 노드를 큐에 삽입하고 방문처리를 함!
- 큐에서 노드를 꺼내 해당 노드의 인접 노드 중에서 방문하지 않은 노드를 모두 큐에 삽입하고 방문처리함
- 2번의 과정을 더 이상 수행할 수 없을 때까지 반복
from collections import deque
# BFS 메서드 정의
def bfs(graph, start, visited):
# 큐 구현을 위해 deque 라이브러리 사용
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) # DFS랑 다르게 방문하지 않았으면 append를 해줌, 재귀함수 x
visited[i] == True
# 각 노드가 연결된 정보를 리스트 자료형으로 표현
graph = [
[],
[2,3,8],
[1,7],
[1,4,5],
[3,5],
[3,4],
[7],
[2,6,8],
[1,7]
]
# 각 노드가 방문된 정보를 리스트 자료형으로 표현(1차원 리스트)
visited = [False]*9
# 정의된 BFS 함수 호출
bfs(graph, 1, visited)
→ 2차원 배열에서의 탐색 문제를 만나면 그래프 형태로 바꿔서 생각하면 풀이방법이 편하다!
실전문제1. 음료수 얼려 먹기
Problem:
N x M 크기의 얼음틀, 구멍이 뚫려 있으면 0, 칸막이가 존재하는 부분은 1로 표시
구멍이 뚫려 있는 부분끼리 상,하,좌,우로 붙어 있는 경우 서로 연결되는 것으로 간주!
얼음 틀의 모양이 주어졌을 때 생성되는 총 아이스크림의 개수 구하기!
Solution:
→ DFS 이용
- 특정한 지점의 주변 상,하,좌,우를 살펴본 뒤에 주변 지점 중에서 값이 ‘0’이면서 아직 방문하지 않은 지점이 있다면 해당 지점을 방문
- 방문한 지점에서 다시 상,하,좌,우를 살펴보면서 방문을 다시 진행하면, 연결된 모든 지점을 방문할 수 있음
- 1~2번 과정을 모든 노드에 반복하며 방문하지 않은 지점의 수를 센다
# DFS 문제 - 연결요소 찾기
# N, M을 공백으로 구분해 입력 받기
N, M = map(int, input().split())
# 2차원 리스트의 맵 정보 입력 받기
graph = []
for i in range(n):
graph.append(list(map(int, input())))
def dfs(x,y): # x,y로 위치 좌표 설정
# 주어진 범위를 벗어나면 즉시 종료 ! (종료 조건 설정!)
if x <= -1 or x >= n or y <= -1 or y >= m:
return False
# 현재 노드를 아직 방문하지 않았다면
if graph[x][y] == 0:
# 해당 노드 방문 처리
graph[x][y] == 1
# 상하좌우 위치 모두 재귀적으로 호출 -> 주변에 있는 노드들이 모두 방문처리 됨
dfs(x-1,y)
dfs(x, y-1)
dfs(x+1, y)
dfs(x, y+1)
return True
return False
# 모든 노드에 대하여 음료수 채우기
result = 0
for i in range(n):
for j in range(m):
# 현재 위치에서 DFS 수행 - 0일때, 처음 방문일때만 for문 돌아감
if dfs(i,j) == True:
result += 1
print(result)
실전문제 2. 미로탈출
Problem:
동빈 NxM 크기의 정사각형 형태의 미로에 갇혀 있음. 미로에 괴물 갇혀 있어서 이를 피해서 탈출 해야 함!
동빈이 위치 : (1,1) 미로의 출구 : (N,M), 한번에 한 칸씩 이동 가능
괴물이 있으면 0, 괴물이 없으면 1
동빈이가 탈출하기 위해 움직여야 하는 최소 칸의 개수? (시작과 마지막 모두 포함하기!)
Solution:
BFS 이용했을 때 효과적! 시작지점에서 가까운 노드부터 차례대로 그래프의 모든 노드를 탐색하기 때문!
특정한 노드를 방문하면 그 이전 노드의 거리에 1을 더한 값을 리스트에 넣기!
[참고자료]
- 이것이 취업을 위한 코딩 테스트다 with 파이썬 / 나동빈
'Algorithms | 자료구조' 카테고리의 다른 글
[자료구조] 스택 / 큐 (0) | 2023.10.29 |
---|---|
[Algorithms] 구현 (1) | 2023.10.15 |
[Algorithms] Dynamic Programming (1) | 2023.10.07 |
- Total
- Today
- Yesterday
- pointwise reranker
- 다중 GROUP BY
- WHERE절서브쿼리
- 알고리즘
- cold-start
- SQL레시피
- 고전적 추천 알고리즘
- reranker
- NULL인 열 만들어주기
- rag 다중문서 활용
- SET문
- 서브쿼리
- groupby 다중
- 추천시스템
- Lagrange Multipler
- 연관규칙분석
- ORDER BY LIMIT
- 숨겨진조건
- llm reranker
- SQL
- reranker 속도 개선
- treer구조
- SASRec
- NULL AS
- 하나의 테이블에 대한 조작
- WHERE문 집계함수
- 이코테
- SELECT문 안 서브쿼리
- 하이브리드 필터링
- 여러개 값에 대한 조작
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |