๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ Algorithm/์•Œ๊ณ ๋ฆฌ์ฆ˜-Swift

[์•Œ๊ณ ๋ฆฌ์ฆ˜/Swift] ๊ทธ๋ž˜ํ”„ํƒ์ƒ‰(DFS/BFS) - ํƒ€๊ฒŸ๋„˜๋ฒ„, ๋„คํŠธ์›Œํฌ, ๋‹จ์–ด๋ณ€ํ™˜, ์—ฌํ–‰๊ฒฝ๋กœ

by Danna 2021. 7. 21.
728x90
728x90

์ฝ”ํ…Œ์—์„œ ์ œ์ผ ๋งŽ์ด ๋‹ค๋ฃจ๋Š” DFS/BFS

1. ํƒ€๊ฒŸ ๋„˜๋ฒ„ (DFS)

  • 1์„ ๋”ํ•˜๊ฑฐ๋‚˜ ๋บ„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋”ํ•˜๊ฑฐ๋‚˜ ๋นผ๋Š” ๊ฒฝ์šฐ์— ๋Œ€ํ•ด ํ•œ๋ฒˆ์”ฉ DFS ๋ฅผ ์ˆ˜ํ–‰ํ•ด์ค€๋‹ค.
  • ์ด๋ฅผ ๊ทธ๋ž˜ํ”„๋กœ ๊ทธ๋ ค๋ณด๋ฉด ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๋‹ค. ์ˆซ์ž๋ฅผ ํ•˜๋‚˜ ๋”ํ•˜๊ฑฐ๋‚˜ ๋นผ๋Š” ๊ฒƒ์„ ๊ทธ๋ž˜ํ”„์˜ ๊นŠ์ด ํƒ์ƒ‰์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

๊ทธ๋ž˜ํ”„์ฒ˜๋Ÿผ ๊ทธ๋ ค๋ณด๊ธฐ

import Foundation

func dfs(numbers: [Int], target: Int, i: Int, total: Int) -> Int {
    if i == numbers.count {
        return total == target ? 1 : 0
    }
    
    let count1 = dfs(numbers: numbers, target: target, i: i+1, total: total - numbers[i])
    let count2 = dfs(numbers: numbers, target: target, i: i+1, total: total + numbers[i])
    return count1 + count2
}

func solution(_ numbers:[Int], _ target:Int) -> Int {
    return dfs(numbers: numbers, target: target, i: 0, total: 0)
}

2. ๋„คํŠธ์›Œํฌ (DFS)

  • ๊ฐ ์ปดํ“จํ„ฐ๋ฅผ ๋…ธ๋“œ๋กœ, ์ปดํ“จํ„ฐ๊ฐ€ ์—ฐ๊ฒฐ๋œ ๋„คํŠธ์›Œํฌ๋ฅผ ๊ทธ๋ž˜ํ”„๋กœ ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.
  • ํŠน์ • ์ปดํ“จํ„ฐ์—์„œ ์‹œ์ž‘ํ•ด ์—ฐ๊ฒฐ๋œ ๋ชจ๋“  ์ปดํ“จํ„ฐ๋ฅผ ํƒ์ƒ‰ํ•˜๋Š” DFS ๋ฐฉ์‹์„ ์ด์šฉํ–ˆ๋‹ค.
  • visited ๋ฐฐ์—ด์„ ํ†ตํ•ด ๋ฐฉ๋ฌธํ•œ ๋…ธ๋“œ๋ฅผ ์ฒดํฌํ•œ ํ›„, ํƒ์ƒ‰ํ•˜๋„๋ก ํ–ˆ๋‹ค.
  • DFS ๋ฅผ ์ด ๋ช‡๋ฒˆ ์ˆ˜ํ–‰ํ–ˆ๋Š”์ง€ ์ฒดํฌํ•˜๋ฉด ๋„คํŠธ์›Œํฌ์˜ ๊ฐœ์ˆ˜๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.
import Foundation

// ๋„คํŠธ์›Œํฌ ๊ฐœ์ˆ˜๋ฅผ return -> DFS ๋กœ ๊ฐ™์€ ๋„คํŠธ์›Œํฌ ์ „๋ถ€ ์ฒดํฌ  
// computers[i][j] : i๋ฒˆ๊ณผ j๋ฒˆ์ด ์—ฐ๊ฒฐ๋˜์–ด์žˆ์œผ๋ฉด 1, ์•„๋‹ˆ๋ฉด 0 

func dfs(_ graph: [[Int]], _ visited: inout [Bool], _ i: Int) {
    if i == graph.count {
        return
    }
    visited[i] = true
    for (j, isConnect) in graph[i].enumerated() {
        if isConnect == 1 && !visited[j] {
            dfs(graph, &visited, j)
        }
    }
}

func solution(_ n:Int, _ computers:[[Int]]) -> Int {
    
    var visited = Array(repeating: false, count: n)
    var count = 0
    
    for i in 0..<n {
        if visited[i] == false {
            dfs(computers, &visited, i)
            count += 1
        }
    }

    return count
}

 

3. ๋‹จ์–ด ๋ณ€ํ™˜ (BFS)

  • begin ๋‹จ์–ด๋Š” words ๋ฐฐ์—ด์— ์—†์–ด, ์ด๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค.
  • ์ฐธ๊ณ ๋กœ target ์ด words ์— ์—†๋‹ค๋ฉด begin -> target ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ 0 ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • ์ฃผ์–ด์ง„ words ๋ฅผ ํ†ตํ•ด ์ด์›ƒ ๊ทธ๋ž˜ํ”„๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค. ๋‹จ์–ด๊ฐ€ ํ•˜๋‚˜๋งŒ ์ฐจ์ด๋‚˜๋Š” ๋‹จ์–ด๋“ค์„ ์—ฐ๊ฒฐํ•ด์ฃผ๋Š” ๊ทธ๋ž˜ํ”„์ด๋‹ค. (๋‹จ์–ด 50๊ฐœ์ดํ•˜)
  • ์ด์›ƒ ๊ทธ๋ž˜ํ”„์— ๋Œ€ํ•ด BFS ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๊ฐ€์žฅ ์งง์€ ๋ณ€ํ™˜ ๊ณผ์ •์„ ์ฐพ๋Š”๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— BFS ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.
  • depth ๊ณ„์‚ฐ์€ ํ˜„์žฌ ๋…ธ๋“œ์—์„œ ์—ฐ๊ฒฐ๋œ ๋…ธ๋“œ๋“ค์„ queue ์— ๋„ฃ์–ด์ค„ ๋•Œ, ํ˜„์žฌ ๋…ธ๋“œ์˜ depth + 1 ๋กœ ๊ณ„์‚ฐํ•ด์ค€๋‹ค. 
  • ๋…ธ๋“œ๋ฅผ ๋ฐฉ๋ฌธํ•˜๋Š”๋Œ€๋กœ ๋‹จ์ˆœํžˆ ๊ณ„์‚ฐํ•ด์ค€๋‹ค๋ฉด, depth ๋ฅผ ๊ณ„์‚ฐํ•  ์ˆ˜ ์—†๋‹ค. (์˜ค๋ฅธ์ชฝ ๊ทธ๋ฆผ์˜ ๊ฒ€์€์ƒ‰์ด ์ด์— ํ•ด๋‹น, ๋นจ๊ฐ„์ƒ‰์ด Depth)
  • ํ•ด๋‹น ๋ฌธ์ œ์—์„œ๋Š” ํ…Œ์ŠคํŠธ 1๋ฒˆ์ด ํ†ต๊ณผํ•˜์ง€ ๋ชปํ•ด๋„, ์ฑ„์ ์ด ๊ฐ€๋Šฅํ•œ ์—๋Ÿฌ๊ฐ€ ์žˆ๋‹ค. (1๋ฒˆ๋„ ํ†ต๊ณผ๋˜์–ด์•ผ ๋งž๋Š” ํ’€์ด๋‹ค ..!)

์˜ค๋ฅธ์ชฝ ๊ทธ๋ฆผ์—์„œ ๋นจ๊ฐ„์ƒ‰ ์ˆซ์ž๊ฐ€ Depth ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

func getNeighbours(_ words: [String]) -> [[Int]] {
    let n = words.count
    var neighbours = Array(repeating: [Int](), count: n)
    for i in 0..<n {
        for j in i+1..<n {
            var count = 0
            zip(words[i], words[j]).forEach { (c1, c2) in
                if c1 != c2 { count += 1 }
            }
            if count == 1 {
                neighbours[i].append(j)
                neighbours[j].append(i)
            }
        }
    }
    return neighbours
}

/// BFS
func findShortestPath(_ neighbours: [[Int]], _ words: [String], _ target: String) -> Int {
    let n = neighbours.count
    var queue = [Int]()
    var depth = Array(repeating: 0, count: n)
    var visited = Array(repeating: false, count: n)

    queue.append(0)
    visited[0] = true
    while !queue.isEmpty {
        let current = queue.removeFirst()
        if words[current] == target {
            return depth[current]
        }
        for next in neighbours[current] {
            if visited[next] == false {
                visited[next] = true
                depth[next] = depth[current] + 1
                queue.append(next)
            }
        }
    }
    return 0
}

func solution(_ begin:String, _ target:String, _ words:[String]) -> Int {

    // Step0. ๋น„๊ธด๋„ ๋‹จ์–ด์— ์ถ”๊ฐ€, ํƒ€๊ฒŸ์ด ์—†๋Š” ๊ฒฝ์šฐ ๋งŒ๋“ค ์ˆ˜ ์—†์Œ
    guard words.contains(target) else {
        return 0
    }
    var words = words
    words.insert(begin, at: 0)

    // Step1. ์•ŒํŒŒ๋ฒณ ํ•˜๋‚˜๋ฅผ ๋ฐ”๊ฟ”์„œ ๋‹จ์–ด๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ, ์ด์›ƒ ๋…ธ๋“œ๋กœ ์„ค์ •!
    let neighbours = getNeighbours(words)

    // Step2. begin๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜์—ฌ ํ•œ ๊ธ€์ž์”ฉ ๋ฐ”๊ฟ”๋‚˜๊ฐ€๋ฉด์„œ target์ด ๋˜๋Š” ์ตœ์†Œ ๊ฒฝ๋กœ๋ฅผ ์ฐพ๊ธฐ!
    return findShortestPath(neighbours, words, target)
}

 


4. ์—ฌํ–‰๊ฒฝ๋กœ (DFS)

  • tickets ๋ฐฐ์—ด์„ ํ‹ฐ์ผ“์˜ ๋„์ฐฉ์ง€์— ๋”ฐ๋ผ ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌํ•œ๋‹ค. * ๊ฒฝ๋กœ๊ฐ€ 2๊ฐ€์ง€ ์ด์ƒ์ผ ๋•Œ ์•ŒํŒŒ๋ฒณ ์ˆœ์„œ๊ฐ€ ์•ž์„œ๋Š” ๊ฒฝ๋กœ๋ฅผ return
  • ํ‹ฐ์ผ“์„ ์‚ฌ์šฉํ–ˆ๋Š”์ง€์— ๋Œ€ํ•ด visited ๋ฐฐ์—ด๋กœ ์ฒดํฌํ•œ๋‹ค. * ์ฃผ์–ด์ง„ ํ•ญ๊ณต๊ถŒ์€ ๋ชจ๋‘ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.
  • ํ‹ฐ์ผ“์— ๋Œ€ํ•ด DFS ์ˆ˜ํ–‰ -> ๊ฐ€๋Šฅํ•œ ๊ฒฝ๋กœ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. 
  • DFS ์ˆ˜ํ–‰์‹œ์—, ๋ชจ๋“  ํ‹ฐ์ผ“์„ ์†Œ์ง„ํ•ด ๊ฒฝ๋กœ๋ฅผ ๋งŒ๋“  ๊ฒฝ์šฐ course ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๊ฒฝ๋กœ๊ฐ€ ๋” ์ด์–ด์ง€์ง€ ์•Š๋Š” ๊ฒฝ์šฐ [] ๋ฅผ ๋ฐ˜ํ™˜.
  • ์ฃผ์˜์‚ฌํ•ญ) ๊ฐ™์€ ๊ฒฝ๋กœ์˜ ํ‹ฐ์ผ“์ด ์ค‘๋ณต๋˜์–ด ์ฃผ์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค.

func findCourse(_ tickets: inout [[String]], _ visited: inout [Bool],
                _ src: String, _ course: [String]) -> [String] {

    if course.count == tickets.count+1 {
        return course
    }

    for (index, ticket) in tickets.enumerated() {
        if ticket[0] == src && visited[index] == false {
            visited[index] = true
            var newCourse = course
            newCourse.append(ticket[1]) // add dst
            let result = findCourse(&tickets, &visited, ticket[1], newCourse)
            if !result.isEmpty { return result }
            visited[index] = false
        }
    }
    return []
}

func solution(_ tickets:[[String]]) -> [String] {

    // ํ‹ฐ์ผ“์˜ ๋„์ฐฉ์ง€์— ๋”ฐ๋ผ ์˜ค๋ฆ„์ฐจ์ˆœ ์ •๋ ฌ.
    var tickets = tickets.sorted(by: { $0[1] < $1[1] })

    // ํ‹ฐ์ผ“์„ ์‚ฌ์šฉํ–ˆ๋Š”์ง€์— ๋Œ€ํ•ด visited ๋ฐฐ์—ด๋กœ ์ฒดํฌ
    var visited = Array(repeating: false, count: tickets.count)

    // ํ‹ฐ์ผ“์— ๋Œ€ํ•ด DFS ์ˆ˜ํ–‰ -> ๊ฐ€๋Šฅํ•œ ๊ฒฝ๋กœ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    return findCourse(&tickets, &visited, "ICN", ["ICN"])
}

 

์—ฌํ–‰๊ฒฝ๋กœ ๋ฐ˜๋ก€

* ํ…Œ์ŠคํŠธ์ผ€์ด์Šค 1, 2 ๋Ÿฐํƒ€์ž„์—๋Ÿฌ์— ๋Œ€ํ•œ ๋ฐ˜๋ก€ ํฌํ•จ

๋”๋ณด๊ธฐ

// ๋ฐ˜๋ก€ 1

print(solution([["ICN", "BBB"],["ICN", "CCC"],["BBB", "CCC"],["CCC", "BBB"],["CCC", "ICN"]]))

// ["ICN", "BBB", "CCC", "ICN", "CCC", "BBB"]

 

// ๋ฐ˜๋ก€ 2

print(solution([["ICN", "SFO"], ["SFO", "ICN"], ["ICN", "SFO"], ["SFO", "QRE"]]))

// ["ICN", "SFO", "ICN", "SFO", "QRE"]

 

// ๋ฐ˜๋ก€ 3

print(solution([["ICN", "BOO"], ["ICN", "COO"], ["COO", "DOO"], ["DOO", "COO"], ["BOO", "DOO"], ["DOO", "BOO"], ["BOO", "ICN"], ["COO", "BOO"]]))

// ["ICN", "BOO", "DOO", "BOO", "ICN", "COO", "DOO", "COO", "BOO"]))

    

// ๋ฐ˜๋ก€ 4 * ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ ํ•ด๊ฒฐ (ZOO ์—์„œ ์ถœ๋ฐœํ•˜๋Š” ํ‹ฐ์ผ“์ด ์—†์–ด ๋”•์…”๋„ˆ๋ฆฌ์ ‘๊ทผx ๋Ÿฐํƒ€์ž„์—๋Ÿฌ ๋ฐœ์ƒ)

print(solution([["ICN", "AOO"], ["AOO", "BOO"], ["AOO", "BOO"], ["BOO", "AOO"], ["BOO", "FOO"], ["FOO", "COO"], ["COO", "ZOO"]]))

// ["ICN", "AOO", "BOO", "AOO", "BOO", "FOO", "COO", "ZOO"]


* DFS / BFS ๋น„๊ต

๊ฐ™์ด ์Šคํ„ฐ๋””ํ•˜๋Š” ํ…Œ์ผ๋Ÿฌ ์Šค์œ„ํ”„ํŠธ๋‹˜์ด ์ •๋ฆฌํ•ด์ฃผ์‹  ๋‚ด์šฉ

728x90
728x90