노트/알고리즘

DFS와 BFS

작성

『이것이 코딩 테스트다』 5장이다. 스택, 큐, 재귀, 그래프 표현 방식 예제를 따라 친 뒤 DFS 와 BFS 를 구현하고 음료수 얼려 먹기와 미로 탈출을 풀었다. 미로 탈출 풀이 끝에는 BFS 가 거리를 격자에 어떻게 적어 가는지 출력해 둔 것이 있다. DFS 예제에는 if not visited[v] 로 적어 둔 버그가 있다. 방문 여부를 확인할 것은 이웃 i 다. 특정 거리의 도시 찾기는 DFS 로 접근한 1차 풀이에서 멈췄다.

스택 예제

stack = []

stack.append(5)
stack.append(2)
stack.append(3)
stack.append(7)
stack.pop() #7
stack.append(1)
stack.append(4)
stack.pop() #4

print(stack) #가장 깊은 곳 부터 출력
print(stack[::-1]) #pop되면서 출력

큐 예제

from collections import deque

queue = deque()

queue.append(5)
queue.append(2)
queue.append(3)
queue.append(7)
# queue.pop() # 7
queue.popleft() # 5
queue.append(1)
queue.append(4)
queue.popleft() # 2

print(queue) # 입력한 순서대로

queue.reverse()
print(queue)

# pop 최근 들어온 것
# popleft 먼저 들어온 것

재귀 예제

def recursive_function(i):
    if i == 100:
        return
    print(f'{i}번째 재귀 함수를 호출합니다')
    recursive_function(i+1)

recursive_function(1)

팩토리얼 예제

# 반복적으로 구현
def factorial_iterative(n):
    result = 1
    for i in range(1,n+1):
        result *= i
    return result

def factorial_recursive(n):
    # if n == 1:
    #     return

    # 1! 0! 인 경우도 계산해야 하기 때문에
    if n <=1 :
        return 1

    return n*factorial_recursive(n-1)


# 재귀로 구현

print(factorial_iterative(5))
print(factorial_iterative(5))

그래프 표현 방식

#인접행렬(Adjacency Matrix)
INF = 999999999

graph = [
    [0,7,5],
    [7,0,INF],
    [5,INF,0]
]

print(graph)
# 인접 리스트(Adjacency List)
graph = [[] for _ in range(3)]

graph[0].append((1,7))
graph[0].append((2,5))

graph[1].append((0,7))

graph[2].append((0,5))

print(graph)

DFS 예제

def DFS(graph,v,visited):
    visited[v] = True
    print(v,end='')

    for i in graph[v]:
        if not visited[v]:
            DFS(graph,i,visited)

graph=[
    [],
    [2,3,8],
    [1,7],
    [1,4,5],
    [3,5],
    [3,4],
    [7],
    [2,6,8],
    [1,7]
]

visited = [False] * 9
DFS(graph,1,visited)

BFS 예제

from collections import deque

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

graph=[
    [],
    [2,3,8],
    [1,7],
    [1,4,5],
    [3,5],
    [3,4],
    [7],
    [2,6,8],
    [1,7]
]

visited = [False] * 9
BFS(graph,1,visited)

음료수 얼려 먹기

# 영역별 카운트가 필요함
# 아이디어: 카운트는 vistied ==False일 때만 시키고,
# visited Ture일 때 연결되어 있는 노드들은 연쇄적으로 True로 만들어 버려서 카운트 되지 않게 하기

n,m = map(int,input().split())

graph=[]
for i in range(n):
    graph.append(list(map(int,input())))

#DFS로 연결된 노드 방문
# index error는 len(array) 이상일 때만, -1은 순서 뒤로 가느 인덱스!!
# def DFS(x,y):
#     try:
#         if graph[x][y] ==0:
#             graph[x][y] = 1
#             DFS(x-1,y)
#             DFS(x+1,y)
#             DFS(x,y-1)
#             DFS(x,y+1)
#             return True
#         else:
#             return False
#     except:
#         pass

def DFS(x,y):
    # if x > -1 or x<n and y>-1 or y<m:
    # 여집합
    if x > -1 and x<n and y>-1 and y<m:
        if graph[x][y] == 0:
            graph[x][y] = 1
            DFS(x-1,y)
            DFS(x+1,y)
            DFS(x,y-1)
            DFS(x,y+1)
            return True
        else:
            return False
    else:
        return

result = 0

for i in range(n):
    for j in range(m):
        if DFS(i,j) == True:
            result +=1

print(result)

미로 탈출

#BFS풀이: 시작지점에서 가까운 노드부터 차례대로 그래프의 모든 노드를 탐색하기 때문
from collections import deque

n,m=map(int,input().split())
graph=[]
for i in range(n):
    graph.append(list(map(int,input())))
    # list(iterative)이므로 input의 한 음절씩 int로 바뀌어 list로 들어가게된다.

# 상하좌우
dx = [-1,1,0,0]
dy = [0,0,-1,1]

def BFS(x,y):
    queue = deque()
    queue.append((x,y))
    while queue:
        x,y=queue.popleft()
        for i in range(4):
            nx = dx[i] + x
            ny = dy[i] + y
            # 공간을 벗어낫거나, 벽인 경우는 continue로 바로 다음 반복 문으로 이동
            if nx<0 or nx>=n or ny<0 or ny>=m or graph[nx][ny]==0:
                continue
            elif graph[nx][ny] ==1: # 0이면 못가는 곳, 2이상이면 이미 방문했던 곳, 딱 1이여야 다음칸으로 이동할 수 있음
                graph[nx][ny] = graph[x][y] + 1 # 한 칸 이동 했음을 표시
                queue.append((nx,ny)) # 현재 이동한 곳이 다음 출발지점이 됌
    return graph[n-1][m-1]

print(BFS(0,0))
for i in range(n):
    print(graph[i])


# [3, 0, 5, 0, 7, 0]
# [2, 3, 4, 5, 6, 7]
# [0, 0, 0, 0, 0, 8]
# [14, 13, 12, 11, 10, 9]
# [15, 14, 13, 12, 11, 10]
# [1,0]에서 상으로 이동할 때 1+2가 되버리기 때문에 3이 됌

특정 거리의 도시 찾기 (1차 풀이에서 멈춤)

# 1차 풀이
# default dict로 사용하는건 DFS안에 for문에서 방문처리할 때.. 가 아니라 단방향거리만 존재니까 다시해보자
from collections import defaultdict
di=defaultdict(list)

n,m,k,x = map(int,input().split())

# 빈 리스트로 초기화
for i in range(1,n+1):
    di[i]

# 유사 딕셔너리로 값 대입
for i in range(m):
    li=list(map(int,input().split()))
    di[li[0]].append(li[1])

count = -1
answer = []
def DFS(x,di,k,count):
    # 최단거리가 같을 때
    global answer
    if k == count:
        answer +=1
        return
    # 다음에 방문할 곳이 있다면
    if di[x] != []:
        for i in di[x]:
            DFS(i,di,k,count+1)
    return

DFS(x,di,k,0)

print(i for i in answer if answer ==[])