λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
πŸ“š Computer Science/Algorithm

[μ•Œκ³ λ¦¬μ¦˜] DFS/ BFS

by hyebin (Helia) 2022. 3. 23.
λ°˜μ‘ν˜•

탐색(Search): λ§Žμ€ μ–‘μ˜ 데이터 μ€‘μ—μ„œ μ›ν•˜λŠ” 데이터λ₯Ό μ°ΎλŠ” κ³Όμ •

  • κ·Έλž˜ν”„, 트리 λ“±μ˜ 자료ꡬ쑰 μ•ˆμ—μ„œ νƒμƒ‰ν•˜λŠ” 문제λ₯Ό 주둜 닀룬닀.
  • λŒ€ν‘œμ μΈ 탐색 μ•Œκ³ λ¦¬μ¦˜μœΌλ‘œ DFS와 BFSκ°€ μžˆλ‹€.

 

두 μ•Œκ³ λ¦¬μ¦˜μ„ μ΄ν•΄ν•˜κΈ° μœ„ν•΄ ν•„μš”ν•œ 자료ꡬ쑰

더보기

자료ꡬ쑰(Data Struture): 데이터λ₯Ό ν‘œν˜„ν•˜κ³  κ΄€λ¦¬ν•˜κ³  μ²˜λ¦¬ν•˜κΈ° μœ„ν•œ ꡬ쑰

 

μŠ€νƒκ³Ό νλŠ” 자료ꡬ쑰의 기초 κ°œλ…μœΌλ‘œ λ‹€μŒ 두  핡심 ν•¨μˆ˜λ‘œ κ΅¬μ„±λœλ‹€.

  • μ‚½μž…(Push): 데이터λ₯Ό μ‚½μž…
  • μ‚­μ œ(Pop): 데이터λ₯Ό μ‚­μ œ

μ˜€λ²„ν”Œλ‘œμš°(Overflow): νŠΉμ • μžλ£Œκ΅¬μ‘°κ°€ μˆ˜μš©ν•  수 μžˆλŠ” λ°μ΄ν„°μ˜ 크기λ₯Ό λ„˜μ€ μƒνƒœμ—μ„œ μ‚½μž… 연산을 μˆ˜ν–‰ν•  λ•Œ λ°œμƒ

μ–Έλ”ν”Œλ‘œμš°(Underflow): νŠΉμ • μžλ£Œκ΅¬μ‘°μ— 데이터가 λ“€μ–΄μžˆμ§€ μ•Šμ€ μƒνƒœμ—μ„œ μ‚­μ œ μ—°μ‚° μˆ˜ν–‰ μ‹œ λ°œμƒ

 

μŠ€νƒ(Stack): 데이터λ₯Ό λ“€μ–΄μ˜¨ μˆœμ„œλŒ€λ‘œ μŒ“κ³  κ°€μž₯ λ§ˆμ§€λ§‰μœΌλ‘œ λ“€μ–΄μ˜¨ 데이터뢀터 λ‚˜κ°€λŠ” ꡬ쑰, μ„ μž… ν›„μΆœ(FILO) ꡬ쑰 λ˜λŠ” ν›„μž… μ„ μΆœ(LIFO) ꡬ쑰라고 ν•œλ‹€.

큐(Queue): κ°€μž₯ λ¨Όμ € λ“€μ–΄μ˜¨ 데이터뢀터 λ‚˜κ°€λŠ” ꡬ쑰, μ„ μž…μ„ μΆœ(FIFO) ꡬ쑰라고 ν•œλ‹€.

 

μž¬κ·€ ν•¨μˆ˜(Recursive Function): 자기 μžμ‹ μ„ λ‹€μ‹œ ν˜ΈμΆœν•˜λŠ” ν•¨μˆ˜, λ¬΄ν•œλŒ€λ‘œ μž¬κ·€ 호좜 μ‹œ 였λ₯˜κ°€ λ°œμƒν•˜κΈ° λ•Œλ¬Έμ— μ’…λ£Œ 쑰건 λͺ…μ‹œκ°€ ν•„μš”ν•˜λ‹€. κ°„κ²°ν•˜κ²Œ μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μžˆλ‹€.

 

 

κ·Έλž˜ν”„: λ…Έλ“œ(Node)와 κ°„μ„ (Edge)으둜 ν‘œν˜„λœλ‹€. 두 λ…Έλ“œκ°€ κ°„μ„ μœΌλ‘œ μ—°κ²°λ˜μ–΄ 있으면 두 λ…Έλ“œλŠ” μΈμ ‘ν•˜λ‹€κ³  ν‘œν˜„

κ·Έλž˜ν”„ 탐색: ν•˜λ‚˜μ˜ λ…Έλ“œλ₯Ό μ‹œμž‘μœΌλ‘œ λ‹€μˆ˜μ˜ λ…Έλ“œλ₯Ό λ°©λ¬Έ

  • 인접 ν–‰λ ¬(Adjacency Matrix): 2차원 λ°°μ—΄λ‘œ κ·Έλž˜ν”„μ˜ μ—°κ²° 관계λ₯Ό ν‘œν˜„ν•˜λŠ” 방식
  • 인접 리슀트(Adjacency List): 리슀트둜 κ·Έλž˜ν”„ μ—°κ²° 관계λ₯Ό ν‘œν˜„ν•˜λŠ” 방식
  • λ©”λͺ¨λ¦¬ μΈ‘λ©΄μ—μ„œ 인접 행렬을 λ…Έλ“œμ˜ κ°œμˆ˜κ°€ λ§Žμ„μˆ˜λ‘ λ©”λͺ¨λ¦¬ λ‚­λΉ„, 인접 리슀트 방식은 효율적
  • 인접 리슀트 방식은 인접 ν–‰λ ¬ 방식에 λΉ„ν•΄ λ…Έλ“œκ°€ μ—°κ²°λ˜μ–΄ μžˆλŠ”μ§€μ— λŒ€ν•œ 정보λ₯Ό μ–»λŠ” 속도가 느림

 

DFS(Depth-First Search)

  • 깊이 μš°μ„  탐색
  • κ·Έλž˜ν”„μ—μ„œ κΉŠμ€ 뢀뢄을 μš°μ„ μ μœΌλ‘œ νƒμƒ‰ν•˜λŠ” μ•Œκ³ λ¦¬μ¦˜
  • νŠΉμ •ν•œ 경둜둜 νƒμƒ‰ν•˜λ‹€κ°€ νŠΉμ • μƒν™©μ—μ„œ μ΅œλŒ€ν•œ κΉŠμˆ™μ΄ λ“€μ–΄κ°€μ„œ λ…Έλ“œλ₯Ό λ°©λ¬Έν•œ ν›„ λ‹€μ‹œ λŒμ•„κ°€ λ‹€λ₯Έ 경둜λ₯Ό νƒμƒ‰ν•˜λŠ” μ•Œκ³ λ¦¬μ¦˜
  • λͺ¨λ“  λ…Έλ“œλ₯Ό λ°©λ¬Έν•˜κ³ μž ν•˜λŠ” κ²½μš°μ— μ‚¬μš©
  • BFS에 λΉ„ν•΄ κ°„λ‹¨ν•˜μ§€λ§Œ, BFS에 λΉ„ν•΄ 느림
  • μŠ€νƒ λ˜λŠ” μž¬κ·€ ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜μ—¬ κ΅¬ν˜„

DFS λ™μž‘ κ³Όμ •

  1. 탐색 μ‹œμž‘ λ…Έλ“œλ₯Ό μŠ€νƒμ— μ‚½μž…ν•˜κ³  λ°©λ¬Έ μ²˜λ¦¬ν•œλ‹€.
  2. μŠ€νƒμ˜ μ΅œμƒλ‹¨ λ…Έλ“œμ— λ°©λ¬Έν•˜μ§€ μ•Šμ€ 인접 λ…Έλ“œκ°€ μžˆλ‹€λ©΄ κ·Έ 인접 λ…Έλ“œλ₯Ό μŠ€νƒμ— λ„£κ³  λ°©λ¬Έ μ²˜λ¦¬ν•œλ‹€. λ°©λ¬Έν•˜μ§€ μ•Šμ€ 인접 λ…Έλ“œκ°€ μ—†λ‹€λ©΄ μ΅œμƒλ‹¨ λ…Έλ“œλ₯Ό κΊΌλ‚Έλ‹€
  3. 2번 과정을 더 이상 μˆ˜ν–‰ν•  수 없을 λ•ŒκΉŒμ§€ λ°˜λ³΅ν•œλ‹€.

 

BFS(Breadth First Search)

  • λ„ˆλΉ„ μš°μ„  탐색
  • κ°€κΉŒμš΄ λ…Έλ“œλΆ€ν„° νƒμƒ‰ν•˜λŠ” μ•Œκ³ λ¦¬μ¦˜
  • 두 λ…Έλ“œ μ‚¬μ΄μ˜ μ΅œλ‹¨ 경둜λ₯Ό 찾을 λ•Œ μ‚¬μš©
  • 큐λ₯Ό μ΄μš©ν•˜μ—¬ κ΅¬ν˜„

BFS λ™μž‘ κ³Όμ •

  1. 탐색 μ‹œμž‘ λ…Έλ“œλ₯Ό 큐에 μ‚½μž…ν•˜κ³  λ°©λ¬Έ μ²˜λ¦¬ν•œλ‹€.
  2. νμ—μ„œ λ…Έλ“œλ₯Ό κΊΌλ‚΄ ν•΄λ‹Ή λ…Έλ“œμ˜ 인접 λ…Έλ“œ 쀑 λ°©λ¬Έν•˜μ§€ μ•Šμ€ μ˜€λ“œλ₯Ό λͺ¨λ‘ 큐에 μ‚½μž…ν•˜κ³  λ°©λ¬Έ μ²˜λ¦¬ν•œλ‹€.
  3. 2번 과정을 더 이상 μˆ˜ν–‰ν•  수 없을 λ•ŒκΉŒμ§€ λ°˜λ³΅ν•œλ‹€.

 

 

DFS와 BFS의 μ‹œκ°„ λ³΅μž‘λ„

  • 두 방식 λͺ¨λ‘ 쑰건 λ‚΄μ˜ λͺ¨λ“  λ…Έλ“œλ₯Ό κ²€μƒ‰ν•œλ‹€λŠ” μ μ—μ„œ μ‹œκ°„ λ³΅μž‘λ„ 동일
  • λ‹€μŒ λ…Έλ“œκ°€ λ°©λ¬Έν•˜μ˜€λŠ”μ§€λ₯Ό ν™•μΈν•˜λŠ” μ‹œκ°„κ³Ό 각 λ…Έλ“œλ₯Ό λ°©λ¬Έν•˜λŠ” μ‹œκ°„μ˜ ν•©
  • 인접 리슀트: O(N+E)
  • 인접 ν–‰λ ¬: O(N²)

 

DFS와 BFS 문제 μœ ν˜•

  • κ·Έλž˜ν”„μ˜ λͺ¨λ“  λ…Έλ“œλ₯Ό λ°©λ¬Έν•˜λŠ” 것이 μ€‘μš”ν•œ 문제 => DFS, BFS
  • 경둜의 νŠΉμ§•μ„ μ €μž₯해둬야 ν•˜λŠ” 문제 => DFS
  • μ΅œλ‹¨κ±°λ¦¬λ₯Ό ꡬ해야 ν•˜λŠ” 문제 => BFS

 


DFS / BFS 문제

음료수 μ–Όλ € λ¨ΉκΈ°

N x M 크기의 μ–ΌμŒ 틀이 μžˆλ‹€. ꡬ멍이 뚫렀 μžˆλŠ” 뢀뢄은 0, 칸막이가 μ‘΄μž¬ν•˜λŠ” 뢀뢄은 1둜 ν‘œμ‹œλœλ‹€.
ꡬλͺ…이 뚫렀 μžˆλŠ” 뢀뢄끼리 상, ν•˜, 쒌, 우둜 λΆ™μ–΄ μžˆλŠ” 경우 μ„œλ‘œ μ—°κ²°λ˜μ–΄ μžˆλŠ” κ²ƒμœΌλ‘œ κ°„μ£Όν•œλ‹€.
μ΄λ•Œ, μ–ΌμŒ ν‹€μ˜ λͺ¨μ–‘이 μ£Όμ–΄μ‘Œμ„ λ•Œ μƒμ„±λ˜λŠ” 총 μ•„μ΄μŠ€ν¬λ¦Όμ˜ 개수λ₯Ό κ΅¬ν•˜λŠ” ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•˜μ‹œμ˜€.
var input = readLine()!.split(separator: " ").map{Int(String($0))!}
var n = input[0], m = input[1]

var graph = [[Int]]()

for i in 0..<n{
    graph.append(readLine()!.map{Int(String($0))!})
}

func dfs(_ x: Int, _ y: Int) -> Bool{
    if x <= -1 || x >= n || y <= -1 || 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
}

var result = 0

for i in 0..<n{
    for j in 0..<m{
        if dfs(i,j) == true{
            result += 1
        }
    }
}

print(result)

미둜 νƒˆμΆœ

λ™λΉˆμ΄λŠ” N x M 크기의 μ§μ‚¬κ°ν˜• ν˜•νƒœμ˜ λ―Έλ‘œμ— κ°‡ν˜€ μžˆλ‹€. λ―Έλ‘œμ—λŠ” 괴물듀이 μžˆμ–΄ 이λ₯Ό ν”Όν•΄ νƒˆμΆœν•΄μ•Ό ν•œλ‹€.
λ™λΉˆμ΄λŠ” (1,1)에, μΆœκ΅¬λŠ” (N, M)에 μ‘΄μž¬ν•˜λ©°, ν•œ λ²ˆμ— ν•œ μΉΈμ”© 이동할 수 μžˆλ‹€. 괴물이 μžˆλŠ” 뢀뢄은 0, μ—†λŠ” 뢀뢄은 1둜 ν‘œμ‹œλœλ‹€.
λ―Έλ‘œλŠ” λ°˜λ“œμ‹œ νƒˆμΆœν•  수 μžˆλŠ” ν˜•νƒœλ‘œ μ œμ‹œλ  λ•Œ, νƒˆμΆœν•˜κΈ° μœ„ν•΄ 움직여야 ν•˜λŠ” μ΅œμ†Œ 칸의 개수λ₯Ό κ΅¬ν•˜μ‹œμ˜€.
//swiftλŠ” 큐λ₯Ό μ œκ³΅ν•˜μ§€ μ•Šμ•„ 직접 κ΅¬ν˜„
struct Queue<T> {
    private var queue: [T] = []
    
    public var count: Int {
        return queue.count
    }
    
    public var isEmpty: Bool {
        return queue.isEmpty
    }
    
    public mutating func enqueue(_ element: T) {
        queue.append(element)
    }
    
    public mutating func dequeue() -> T? {
        return isEmpty ? nil : queue.removeFirst()
    }
}


var input = readLine()!.split(separator: " ").map{Int(String($0))!}
var n = input[0], m = input[1]

var graph = [[Int]]()

for i in 0..<n{
    graph.append(readLine()!.map{Int(String($0))!})
}

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

func bfs(_ x: Int, _ y: Int) -> Int{
    var queue = Queue<(Int,Int)>()
    queue.enqueue((x,y))
    
    while !queue.isEmpty{
        var (x,y) = queue.dequeue()!
        
        for i in 0..<4{
            var nx = x + dx[i]
            var ny = y + dy[i]
            
            if nx < 0 || ny < 0 || nx >= n || ny >= m{ continue }
            if graph[nx][ny] == 0{continue}
            if graph[nx][ny] == 1 {
                graph[nx][ny] = graph[x][y] + 1
                queue.enqueue((nx,ny))
            }
        }
        
    }
    return graph[n-1][m-1]
}
print(bfs(0, 0))
λ°˜μ‘ν˜•