본문 바로가기
파이썬 알고리즘

DFS와 BFS

by s2jinny 2022. 7. 27.

탐색: DFS와 BFS 

데이터 탐색(그래프 탐색) = 원하는 데이터 도달 

DFS(Depth-First Search)

; 깊이 우선 탐색은 그래프에서 깊은 부분을 우선으로 탐색

BFS(Breadth-First Search)

; 너비 우선 탐색은 그래피에서 가까운 노드를 우선으로 탐색

DFS:

# DFS 메서드 정의
def dfs(graph, v, visited):
    visited[v] = True # 현재 노드를 방문 처리
    print('DFS 탐색 순서', [v], v, end=' ')
 # 현재 노드와 연결된 다른 노드를 재귀적으로 방문
    for i in graph[v]:
        if not visited[i]:
            print('방문 하지 않은 노드 발견 : ', graph[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 함수 호출, 시작 노드 1
dfs(graph, 1, visited)

-> 파이썬은 스택 구현을 위해 일반적으로 리스트를 활용한다.

실행 결과 :

DFS 탐색 순서 [1] 1 방문 하지 않은 노드 발견 :  [1, 7]
DFS 탐색 순서 [2] 2 방문 하지 않은 노드 발견 :  [2, 6, 8]
DFS 탐색 순서 [7] 7 방문 하지 않은 노드 발견 :  [7]
DFS 탐색 순서 [6] 6 방문 하지 않은 노드 발견 :  [1, 7]
DFS 탐색 순서 [8] 8 방문 하지 않은 노드 발견 :  [1, 4, 5]
DFS 탐색 순서 [3] 3 방문 하지 않은 노드 발견 :  [3, 5]
DFS 탐색 순서 [4] 4 방문 하지 않은 노드 발견 :  [3, 4]
DFS 탐색 순서 [5] 5 


DFS - 음료수 얼려먹기

문제:N x M 크기의 얼음틀이 있다. 얼음 틀의 모양이 주어졌을 때 생성되는 총 아이스크림의 개수를 구하는 프로그램을 작성하세요.

구멍이 뚫려있는 부분은 0, 그렇지 않은 부분은 1이다.

입력예)                            출력 예)

4,5                                        3

00110

00011

11111

00000

0 1 1 0
0 0 0 1 1
1 1 1 1 1
0 0 0 0 0

 

코드 구현 

1. DFS OR BFS (추가 조건이 없다면 DFS)

2.얼음틀 크기 입력 받기

3. 맵 내부 정보 입력받기

4. 상하좌우 값이 0이면 방문

5. 이후 방문한 노드에서 상하좌우 방문

6. 반복하여 모든 지점 방문

7. 방문하지 않은 지점의 수를 카운트 

# DFS로 특정 노드를 방문하고 연결된 모든 노드들도 방문
def dfs(x, y):
# 얼음틀에서 벗어나는 경우에는 즉시 종료
    if x <= -1 or x >= n or y <= -1 or y >= m:
        return False
# 현재 노드를 아직 방문하지 않았다면
    if graph[x][y] == 0: # 처음엔 0, 0
# 해당 노드 방문 처리, 얼음 표시
        graph[x][y] = 1 # 방문 이후 1, 1
# 상, 좌, 하, 우의 위치 재귀 호출(모든 방향 탐색)
        dfs(x - 1, y)
        dfs(x, y - 1)
        dfs(x + 1, y)
        dfs(x, y + 1)
        return True # 방문 끝나면 참 리턴
    return False # 재귀 탈출 조건

# N, M을 공백을 기준으로 구분하여 입력 받기
n, m = map(int, input().split())
# 2차원 리스트의 맵 정보 입력 받기
graph = []
for i in range(n):
    graph.append(list(map(int, input())))
# 모든 노드(위치)에 대하여 음료수 채우기
result = 0
for i in range(n):
    for j in range(m):
# 현재 위치에서 DFS 수행
        if dfs(i, j) == True:
            result += 1
print(result) # 정답 출력

BFS:

from collections import deque # deque를 사용
# BFS 메서드 정의
def bfs(graph, start, visited):
# 큐(Queue) 구현을 위해 deque 라이브러리 사용
    queue = deque([start])
# 현재 노드를 방문 처리
    visited[start] = True
# 큐가 빌 때까지 반복
    while queue:
# 큐에서 하나의 원소를 뽑아 출력하기
        v = queue.popleft()
        print('BFS 탐색 노드 :', v)
# 아직 방문하지 않은 인접한 원소들을 큐에 삽입
        for i in graph[v]:
            if not visited[i]:
                queue.append(i)
                print('새로운 노드 발견 : ', [i], queue)
                visited[i] = True

# 각 노드가 연결된 정보를 표현 (2차원 리스트)
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)

실행결과:

BFS 탐색 노드 : 1
새로운 노드 발견 :  [2] deque([2])
새로운 노드 발견 :  [3] deque([2, 3])
새로운 노드 발견 :  [8] deque([2, 3, 8])
BFS 탐색 노드 : 2
새로운 노드 발견 :  [7] deque([3, 8, 7])
BFS 탐색 노드 : 3
새로운 노드 발견 :  [4] deque([8, 7, 4])
새로운 노드 발견 :  [5] deque([8, 7, 4, 5])
BFS 탐색 노드 : 8
BFS 탐색 노드 : 7
새로운 노드 발견 :  [6] deque([4, 5, 6])
BFS 탐색 노드 : 4
BFS 탐색 노드 : 5
BFS 탐색 노드 : 6


BFS-미로탈출

문제: 모험가(1, 1)위치 기준으로 N X M 크기 미로에서 괴물을 피해 탈출해야 합니다. 탈출하기 위한 최소 칸의 개수를 구하시오

괴물이 있는 부분은 0, 없는 부분은 1로 표시

 

최단 거리를 구하는 문제-> 되도록 BFS 사용하자. (속도문제)

입력 예) 

5 6

101010

111111

000001

111111

111111

출력 예)

10

1 0 1 0 1 0
1 1 1 1 1 1
0 0 0 0 0 1
1 1 1 1 1 1
1 1 1 1 1 1

 

코드 구현

1. 미로 크키 입력

2. 맵 내부 정보 입력

3. 모든 노드 초기화 (bfs)

4. 초기조건 (0,0) 가정하고 

5. 좌표 이탈하면 컨티뉴

6. 상하좌우 방문

7. 최단거리

from collections import deque 

def bfs(x,y):
    queue=deque() #deque 라이브러리 사용 
    queue.append((x,y))
    while queue: #큐가 빌 때까지 반복 
        x,y=queue.popleft()
        #4가지 방향으로 위치 확인 
        for i in range(4): 
            nx=x+dx[i]
            ny=y+dy[i]
            #공간을 벗어나면 무시 
            if nx<0 or nx>=n or ny<0 or ny>=m:
                continue
            #벽인 경우==0 무시
            if graph[nx][ny]==0:
                continue 
            #1인 경우만 처음 방문하는 경우에만 최단거리 기록
            if graph[nx][ny]==1:
                graph[nx][ny]=graph[x][y]+1 #거리를 한 칸 증가시킴
                queue.append((nx,ny))
    #가장 오른쪽 아래까지의 최단거리 반환
    return graph[n-1][m-1]

n,m=map(int,input().split()) 
graph=[] 
for i in range(n):
    graph.append(list(map(int,input()))) #맵 정보 입력

#네가지 방향 
dx=[-1,1,0,0] 
dy=[0,0,-1,1]
print(bfs(0,0))

문제: 내집은 어디에? 아파트 단지 지도를 입력하여 단지의 개수와 단지별로 집의 개수를 출력하는 프로그램을 작성하시오. (DFS와 BFS 두가지로 구현) 

집은 1, 집이 아닌 곳은 0으로 표시함 연결된 아파트는 같은 단지를 의미함.

 

0 1 1 0 1 0 0
0 1 1 0 1 0 1
1 1 1 0 1 0 1
0 0 0 0 1 1 1
0 1 0 0 0 0 0
0 1 1 1 1 1 0
0 1 1 1 0 0 0
 

코드 구현

1.단지크기 입력받기

2. 단지 정보 입력받기 

3.상하좌우 방문

4.DFS-좌표이탈>무시

5.상하좌우 방문

6.집발견하면 갯수 증가

7.집 개수 출력

 

DFS 구현 코드:

def dfs(x,y,L):
    dx=[-1,0,1,0] #4가지 방향 정의
    dy=[0,1,0,-1]
    for i in range(4): #상하좌우 검사
        nx=x+dx[i]
        ny=y+dy[i]
        if 0<=nx<n and 0<=ny<n and arr[nx][ny]==1: #새로운 집 발견 
            arr[nx][ny]=0
            cnt_arr[L]+=1 #다음 좌표 설정
            dfs(nx,ny,L) #dfs 재귀 

n=int(input("단지 크기 입력> "))
arr=[list(map(int,input("단지 지도 세부정보 입력: "))) for i in range(n)]
total_cnt=0
cnt_arr=list() 

for i in range(n):
    for j in range(n):
        if arr[i][j]==1:
            arr[i][j]=0
            cnt_arr.append(1) # 초기 방문 표시 
            dfs(i,j,total_cnt) #dfs 탐색 시작 
            total_cnt+=1 
print("총단지수 : ",total_cnt)
cnt_arr.sort() 
for i in cnt_arr:
    print("각 집의 갯수: ",i)

BFS 구현 코드:

from collections import deque 

n=int(input("단지 크기 입력: "))
arr=[list(map(int,input("단지 세부 정보 입력:")))for i in range(n)]
cnt=0 #단지 안에 속하는 집의 수 
cnt_arr=list() #큐가 ㄱ끝날 때 최종 cnt를 담아줄 배열의 값
dx=[-1,0,1,0]
dy=[0,1,0,-1]

q=deque() 
for i in range(n):
    for j in range(n):
        if arr[i][j]==1:
            q.append((i, j)) # 초기 방문 노드 큐에 추가 
            arr[i][j]=0
            cnt=1
            while q: # 큐가 존재
                x,y=q.popleft() #큐의 현재 노드를 꺼냄 
                for z in range(4):
                    nx=x+dx[z]
                    ny= y+dy[z]
                    if 0<=nx<n and 0<=ny<n and arr[nx][ny]==1:
                        arr[nx][ny]=0 
                        cnt+=1 
                        q.append((nx,ny))         
cnt_arr.sort() 
print("총 단지수 : ",len(cnt_arr))
for i in cnt_arr:
    print("각 집의 갯수:",i)

 

'파이썬 알고리즘' 카테고리의 다른 글

DFS와 BFS  (0) 2022.07.25
구현  (0) 2022.07.21
그리디 알고리즘  (0) 2022.07.21
다이나믹 프로그래밍  (2) 2022.07.21