프로그래밍 언어/자료구조 & 알고리즘

[알고리즘 기초] 1. 그래프 탐색 알고리즘 DFS/BFS

윤창이 2022. 11. 15. 16:29
728x90

[주의] 개인 공부를 위해 쓴 글이기 때문에 주관적인 내용은 물론, 쓰여진 정보가 틀린 것일 수도 있습니다!

피드백 부탁드립니다. (- -)(_ _) 꾸벅

 


1. Stack & Queue 자료구조

Stack: FILO (First In Last Out)

Queue: FIFO (First In First Out) 

from collections import deque

queue = deque()

queue.append(1)
queue.append(2)
queue.popleft()
queue.append(3)
queue.popleft()
print(queue) # deque([3])

###

stack = deque()

stack.append(1)
stack.append(2)
stack.pop()
print(stack)  # deque([1])

리스트 자료형 이용할 수 있지만 시간복잡도가 더 효율적인 deque 이용

 

 


2. Recursive Function (재귀함수)

자기 자신을 다시 호출하는 함수.

실제로 컴퓨터 메모리 스택 상에 이러한 함수가 반복적으로 담겨서 처리함. → 스택 객체로도 표현 가능

반드시 재귀함수 종료 조건을 명시해야 함

def recursive_function(i):
    
    if i == 100:
        return

    print(i, "번째 함수에서 ", i+1, "번째 재귀함수를 호출합니다.")
    recursive_function(i+1)
    print(i, "번째 재귀함수를 종료합니다.")

recursive_function(0)

 

1) 팩토리얼 함수

  • 팩토리얼(!) 구현을 재귀함수를 통해 구현
def factorial(i):
    if i == 1:
        return 1 

    return i * factorial(i-1)
    

print(factorial(5))   # 120

 

2) 최대공약수 계산 (유클리드 호제법)

  • 두 자연수 A, B에 대해 (A > B) A를 B로 나눈 나머지를 R
  • 이때 A와 B의 최대공약수는 B와 R의 최대공약수와 같음

 

단계 A B
1 192 162
2 162 30
3 30 12
4 12 6
def greatest_common_multiple(A, B):
    if A % B == 0:
        return B
    return greatest_common_multiple(B, A % B)

print(greatest_common_multiple(192, 162))

 


3. DFS (Depth-First Search)  &  BFS (Breadth-First Search)

- 대표적인 그래프 탐색 알고리즘

DFS: 깊이 우선 탐색이라고 부르며, 깊은 부분을 우선적으로 탐색하는 알고리즘

BFS: 너비 우선 탐색이라고도 부르며, 그래프에서 가까운 노드부터 우선적으로 탐색하는 알고리즘

DFS(깊이우선탐색) BFS(너비우선탐색)
현재 정점에서 갈 수 있는 점들까지 들어가면서 탐색 현재 정점에 연결된 가까운 점들부터 탐색
스택 또는 재귀함수로 구현 를 이용해서 구현

 

다음은 아래에 그래프 자료구조를 DFS & BFS로 구현하는 방식이다

1) DFS

  1. 탐색 시작 노드를 스택에 삽입하고 방문 처리
  2. 스택의 최상단 노드에 방문하지 않은 인접한 노드가 하나라도 있으면 그 노드를 스택에 넣고 방문 처리. 방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드를 꺼냄
  3. 더 이상 2번의 과정을 수행할 수 없을 때까지 반복
graph = [
    [],
    [2, 3, 8],
    [1, 7],
    [1, 4, 5],
    [3, 5],
    [3, 4],
    [7],
    [2, 6, 8],
    [1, 7]
]

visited = [False] * 9

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

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

bfs(graph, 1, visited)
탐색 순서: 1 -> 2 -> 7 -> 6 -> 8 -> 3 -> 4 -> 5

 

재귀함수는 앞서 설명과 같이 컴퓨터 메모리에 스택 상에 저장하여 동작하는 방식이므로 DFS에서 사용하는 스택 자료구조를 재귀함수로도 표현이 가능하다. (재귀함수가 더 간결하게 코드를 짤 수 있다,)  스택은 아래를 참조

더보기

- Stack을 이용하여 DFS 구현

visited = [False] * 9
def dfs_stack(graph, v, visited):
	stack = deque([v])
	#visited[v] = True

	while stack:
		print(list(stack))
		e = stack.pop()
			
		if not visited[e]:
			print(e, end=' ')
			visited[e] = True
			stack.extend(graph[e][::-1])
			# 원래 일일이 append 하는걸 extend로 뒤집어서 붙인다는 마인드

dfs_stack(graph, 1, visited)
탐색 순서: 1 -> 2 -> 7 -> 6 -> 8 -> 3 -> 4 -> 5

 

2) BFS

  1. 탐색 시작 노드를 큐에 삽입하고 방문 처리를 합니다.
  2. 큐에서 노드를 꺼낸 뒤에 해당 노드의 인접 노드 중에는 방문하지 않은 노드를 모두 큐에 삽입하고 방문 처리합니다.
  3. 더 이상 2번의 과정을 수행할 수 없을 때까지 반복합니다.
from collections import deque

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

visited = [False] * 9

def bfs(graph, v, visited):
	Queue = deque([v])
	visited[v] = True

	while Queue:
		e = Queue.popleft()
		print(e, end=' ')
		
		for i in graph[e]:
			if not visited[i]:
				Queue.append(i)
				visited[i] = True

bfs(graph, 1, visited)
탐색 순서: 1 -> 2 -> 3 -> 8 -> 7 -> 4 -> 5 -> 6

 

3) 예제문제

 

1) 음료수 얼려먹기 문제

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

pan = list()
for _ in range(n):
	pan.append(list(map(int, input())))

def dfs(x, y):
	global pan
	find = False

	if x<0 or x>=n or y<0 or y>=m: pass 
	elif pan[x][y] == 0:
		find = True
		pan[x][y] = 1
		dfs(x+1, y)
		dfs(x, y+1)
		dfs(x-1, y)
		dfs(x, y-1)
	return find

cnt=0
for dx in range(n):
	for dy in range(m): 
		if dfs(dx,dy) : cnt += 1

print(cnt)

 

 

2) 미로탈출 문제

from collections import deque

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

pan = list()
for _ in range(n):
	pan.append(list(map(int, input())))

dx = [1,0,-1,0]
dy = [0,1,0,-1]

def print_pan():
	for i in range(n):
		for j in range(m):
			print(pan[i][j], " ", end='')
		print()
	print("---")

def bfs(x, y):
	queue = deque()
	queue.append((x,y))
	
	while queue:
		x, y = queue.popleft()
		print_pan()
		for i in range(4):
			nx = x + dx[i]
			ny = y + dy[i]
			
			if nx<0 or ny<0 or nx>=n or ny>=m: continue
			elif pan[nx][ny] == 0: continue
			elif pan[nx][ny] == 1 and not (nx==0 and ny==0):
				pan[nx][ny] = pan[x][y] + 1
				queue.append((nx,ny))
	print(pan[n-1][m-1])

bfs(0,0)

 

728x90