본문 바로가기
개발/코딩테스트

[Python] 이것이 코딩테스트다 with 파이썬 - 탐색 알고리즘 DFS/BFS - DFS(Depth-First-Search)편

by seopport 2023. 1. 21.
728x90
반응형

* 해당 문제 및 해설은 "이것이 코딩 테스트 다 with 파이썬" 책을 기준으로 요약 및 정리 하여 작성하였습니다.  

 

http://www.yes24.com/product/goods/91433923

 

이것이 취업을 위한 코딩 테스트다 with 파이썬 - YES24

나동빈 저자의 유튜브 라이브 방송 https://www.youtube.com/c/dongbinnaIT 취준생이라면 누구나 입사하고 싶은 카카오 · 삼성전자 · 네이버 · 라인!취업의 성공 열쇠는 알고리즘 인터뷰에 있다!IT 취준생

www.yes24.com

 

총 2편으로 나누어서 작성할 예정입니다. ( 모든 이미지는 PPT 를 활용하였습니다.)

  1. DFS(Depth-First-Search) 편  - 현재
  2. BFS (Breath-First-Search) 편

탐색 알고리즘 DFS / BFS ★

코딩테스트 단골 손님 탐색 알고리즘 DFS / BFS

스택과 큐, 재귀함수가 DFS / BFS 의 중요한 개념이기 때문에, 모르겠고 기초부터 해야한다면 글을 읽어보길 바란다.

 

DFS

DFS 는 Depth-First Search, 깊이 우선 탐색이라고도 부르며, 그래프에서 깊은 부분을 우선적으로 탐색하는 알고리즘 이다.

 

  • DFS 를 설명하기 전에 먼저 그래프(Graph)의 기본 구조를 알아야 한다.
    • 그래프는 노드 (Node) 와 간선 (Edge)으로 표현되며 이 때 노드를 정점(Vertex) 이라고도 말한다.
    • 두 노드가 간선으로 연결되어 있다면 '두 노드는 인접 하다' 라고 표현한다.

seopport' blog 간선, 노드 설명

 

  • 프로그래밍 에서 그래프는 크게 2가지 방식으로 표현할 수 있는데 코딩 테스트에서는 이 두 방식 모두 필요하니 두 개념에 대해 바르게 알고 있어야 한다.
     
    1. 인접 행렬 ( Adjacency Matrix ) : 2차원 배열로 그래프의 연결  관계를 표현하는 방식
    2. 인접 리스트 ( Adjacency List ) : 리스트로 그래프의 연결 관계를 표현하는 방식 

왼쪽 그림 : 인접 행렬 / 오른쪽 그림 : 인접 리스트

 

  • 인접 행렬 방식은 2차원 배열에 각 노드가 연결된 형태로 기록하는 방식이다. 
  • 아래는 인접 행렬 방식 예제 코드 입니다.
INF = 99999999 # 무한의 비용 선언

# 2차원 리스트를 이용해 인접 행렬 표현

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

print(graph)


# 출력 값
[[0, 7, 5], [7, 0, 999999999], [5, 999999999, 0]]

 

  • 인접 리스트 방식 에서는 다음 그림 처럼 모든 노드에 연결된 노드에 대한 정보를 차례대로 연결하여 저장한다.

  • 다른 언어의 배열(Array)을 파이썬에서는 리스트 자료형으로 표현할 수 있으므로 파이썬은 인접 행렬을 리스트로 구현한다.
  • 아래는 인접 리스트 방식 예제 코드 입니다.

 

# 행(Row) 이 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)


# 출력
[[(1, 7), (2, 5)], [(0,7)], [(0,5)]]

 

이 두방식은 어떤 차이가 있을까?

메모리 측면에서 보자면 인접 행렬 방식은 모든 관계를 저장하므로 노드 개수가 많을 수록 메모리가 불필요하게 낭비된다.

반면에 인접 리스트 방식은 연결된 정보만을 저장하기 때문에 메모리를 효율적으로 사용한다.

하지만 이와 같은 속성 때문에 인접 리스트 방식은 인접 행렬 방식에 비해 특정한 두 노드가 연결 되어 있는지에 대한 정보를 얻는 속도가 느리다.

 

DFS는 깊이 우선 탐색 알고리즘 이다.

알고리즘은 특정한 경로로 탐색하다가 특정한 상황에서 최대한 깊숙이 들어가서 노드를 방문한 후, 다시 돌아가 다른 경로로 탐색하는 알고리즘이다.

 

DFS는 스택 자료 구조를 이용하며 구체적인 동작 과정은 다음과 같다.

  1. 탐색 시작 노드를 스택에 삽입하고 방문 처리를 한다.
  2. 스택의 최상단 노드에 방문하지 않은 인접 노드가 있다면 그 인접 노드를 스택에 넣고 방문 처리를 한다.
    방문하지 않은 인접 노드가 없으면 스택에서 최상단 노드를 꺼낸다.
  3. 2번의 과정을 더 이상 수행할 수 없을 때까지 반복한다.

 

아래 이미지를 보고 참고 하시면, 이해하기 편하실 듯 합니다.

 

단계 1  시작 노드인 '1' 을 스택에 삽입하고 방문 처리를 한다.

단계 2  스택의 최상단 노드인 '1' 에 방문하지 않은 인접 노드 '2', '3', '8' 이 있다.
            이 중에서 가장 작은 노드인 '2' 를 스택에 넣고 방문 처리를 한다.

단계 3  스택에 최상단 노드인 '2' 에 방문하지 않은 인접 노드 '7' 이 있다.
            따라서 '7' 번 노드를 스택에 넣고 방문 처리 한다.

단계 4  스택에 최상단 노드인 '7' 에 방문하지 않은 인접 노드 '6' 과 '8' 이 있다.
            이 중에서 작은 노드인 '6' 번 노드를 스택에 넣고 방문 처리 한다.

단계 5  스택의 최상단 노드인 '6'에 방문하지 않은 인접 노드가 없다.
            따라서 '6' 번 노드를 꺼낸다.

단계 6  스택의 최상단 노드인 '7'에 방문하지 않은 인접 노드가 '8' 이 있다.
            따라서 '8' 번 노드를 스택에 넣고 방문 처리를 한다.

단계 7  스택의 최상단 노드인 '8' 에 방문하지 않은 인접 노드가 없다.
            따라서 스택에서 '8'번 노드를 끄낸다.

단계 8  스택의 최상단 노드인 '7' 에 방문하지 않은 인접 노드가 없다.
            따라서 스택에서 '7'번 노드를 끄낸다.

단계 9  스택의 최상단 노드인 '2' 에 방문하지 않은 인접 노드가 없다.

            따라서 스택에서 '2'번 노드를 끄낸다.

단계 10  스택의 최상단 노드인 '1'에 방문하지 않은 인접 노드가 '8' 이 있다.

              따라서 '3' 번 노드를 스택에 넣고 방문 처리를 한다.

단계 11  스택에 최상단 노드인 '3' 에 방문하지 않은 인접 노드 '4' 과 '5' 이 있다.
             이 중에서 작은 노드인 '4' 번 노드를 스택에 넣고 방문 처리 한다.

단계 12  스택에 최상단 노드인 '4' 에 방문하지 않은 인접 노드 '5' 가 있다.
              따라서 '5' 번 노드를 스택에 넣고 방문 처리를 한다.

단계 13  남아 있는 노드에 방문하지 않은 인접 노드가 없다.
              따라서 모든 노드를 차례대로 꺼내면 다음과 같다.

결과적으로 노드의 탐색 순서 (스택에 들어간 순서)는 다음과 같다.

  • 1 -> 2 -> 7 -> 6 -> 8 -> 3 -> 4 -> 5

DFS 예제 코드

# DFS 메서드 정의

def dfs(graph, v, visited):
	# 현재 노드를 방문 처리
    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)
 
 # 출력 값
 1 2 7 6 8 3 4 5
728x90
반응형