DFS/ BFS

πŸ”‘λŒ€ν‘œμ  κ·Έλž˜ν”„(Graph) 탐색 μ•Œκ³ λ¦¬μ¦˜μΈ DFS/BFSλ₯Ό μ†Œκ°œν•©λ‹ˆλ‹€.

κ·Έλž˜ν”„(Graph)λŠ” 뭘까?

  • κ·Έλž˜ν”„(Graph) λŠ” 정점(vertex, λ…Έλ“œ)κ³Ό κ°„μ„ (edge)둜 κ΅¬μ„±λœ μœ ν•œν•œ(finite) 자료ꡬ쑰 μž…λ‹ˆλ‹€.

  • 두 정점(λ…Έλ“œ)κ°€ κ°„μ„ μœΌλ‘œ μ—°κ²°λ˜λ©΄ '두 λ…Έλ“œλŠ” 인접(Adjacent)ν•˜λ‹€' 라고 ν•©λ‹ˆλ‹€.

  • 페이슀뢁, μΈμŠ€νƒ€κ·Έλž¨ 같은 μ†Œμ…œ λ„€νŠΈμ›Œν¬μ˜ λ°μ΄ν„°λ² μ΄μŠ€κ°€ κ·Έλž˜ν”„ ꡬ쑰둜 λ§Œλ“€μ–΄μ Έ μžˆμŠ΅λ‹ˆλ‹€. κ·Έλž˜ν”„ ꡬ쑰λ₯Ό 톡해 μ‚¬λžŒλ“€(node) μ‚¬μ΄μ˜ 관계(edge) λ₯Ό μ‰½κ²Œ 탐색할 수 μžˆμŠ΅λ‹ˆλ‹€.

κ·Έλž˜ν”„ 탐색

  • ν•˜λ‚˜μ˜ 정점(λ…Έλ“œ)μ—μ„œ λͺ¨λ“  λ…Έλ“œλ₯Ό ν•œ λ²ˆμ”© 탐색(λ°©λ¬Έ) ν•˜λŠ” 것을 λ§ν•©λ‹ˆλ‹€.

  • κ·Έλž˜ν”„μ˜ μ΅œλŒ€ κΉŠμ΄κΉŒμ§€ νƒμƒ‰ν•œ ν›„, λ‹€λ₯Έ 경둜둜 μ΄λ™ν•˜μ—¬ νƒμƒ‰ν•˜λŠ” μ•Œκ³ λ¦¬μ¦˜μž…λ‹ˆλ‹€.

    • μ‹œμž‘ λ…Έλ“œμ—μ„œ μ΅œλŒ€ν•œ 멀리 μžˆλŠ” λ…Έλ“œ λ₯Ό μš°μ„  νƒμƒ‰ν•©λ‹ˆλ‹€.

  • DFSλ₯Ό ν™œμš©ν•˜μ—¬ 미둜찾기 문제λ₯Ό ν•΄κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 미둜 μ†μ˜ 길을 κ°„μ„ (edge)으둜, 막닀λ₯Έ 지점을 정점(node)으둜 보고 미둜의 도착점에 도달할 λ•ŒκΉŒμ§€ 각 경둜의 μ΅œλŒ€ κΉŠμ΄κΉŒμ§€ νƒμƒ‰ν•©λ‹ˆλ‹€. ν˜„μž¬ μ„ νƒν•œ κ²½λ‘œκ°€ 막닀λ₯Έ 골λͺ©μ— λΆ€λ”ͺ히면 λ˜λŒμ•„κ°€μ„œ(λ°±νŠΈλž˜ν‚Ή) λ‹€λ₯Έ 경둜λ₯Ό νƒμƒ‰ν•©λ‹ˆλ‹€.

  • μŠ€νƒκ³Ό μž¬κ·€ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜μ—¬ κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • DFS κ΅¬ν˜„ μ˜ˆμ‹œ

    class Graph {
      constructor() {
        this.adjacencyList = {};
      }
      // 정점(λ…Έλ“œ) μΆ”κ°€
      addVertex(vertex) {
        if (!this.adjacencyList[vertex]) this.adjacencyList[vertex] = [];
        return this.adjacencyList;
      }
      // κ°„μ„  μΆ”κ°€
      addEdge(vertex1, vertex2) {
        this.adjacencyList[vertex1].push(vertex2);
        this.adjacencyList[vertex2].push(vertex1);
        return this.adjacencyList;
      }
    
      // 탐색 μ‹œμž‘ λ…Έλ“œ 인자
      DFS(start) {
        const result = []; // 탐색 κ²°κ³Ό λ°°μ—΄
        const visited = {}; // λ°©λ¬Έν•œ λ…Έλ“œ 체크(각 λ…Έλ“œλ₯Ό ν•œ λ²ˆμ”©λ§Œ μ²˜λ¦¬ν•˜λ„λ‘)
        const adjacencyList = this.adjacencyList;
    
        function dfs(vertex) {
          // 정점이 빈 값이면 null을 λ°˜ν™˜
          if (!vertex) return null;
          // μ‹œμž‘ λ…Έλ“œλ₯Ό μŠ€νƒμ— pushν•˜κ³  λ°©λ¬Έ 처리
          visited[vertex] = true;
          // 탐색 κ²°κ³Ό 배열에 λ…Έλ“œ push
          result.push(vertex);
          // 인접 리슀트λ₯Ό μˆœνšŒν•˜λ©΄μ„œ ν˜„μž¬ λ…Έλ“œμ™€ μ—°κ²°λœ λ‹€λ₯Έ λ…Έλ“œ λ°©λ¬Έ 처리
          adjacencyList[vertex].forEach((v) => {
            // λ°©λ¬Έν•˜μ§€ μ•Šμ€ 인접 λ…Έλ“œ λ°©λ¬Έ 처리(μž¬κ·€)
            if (!visited[v]) dfs(v);
          });
        }
        dfs(start);
        return result;
      }
    }
    const graph = new Graph();
    graph.addVertex("A");
    graph.addVertex("B");
    graph.addVertex("C");
    graph.addVertex("D");
    graph.addVertex("E");
    graph.addVertex("F");
    
    graph.addEdge("A", "B");
    graph.addEdge("A", "C");
    graph.addEdge("B", "D");
    graph.addEdge("C", "E");
    graph.addEdge("D", "E");
    graph.addEdge("D", "F");
    graph.addEdge("E", "F");
    
    graph.DFS("A")  //  ["A", "B", "D", "E", "C", "F"]
  • κ·Έλž˜ν”„μ˜ κ·Όμ ‘ λ…Έλ“œλΆ€ν„° νƒμƒ‰ν•œ ν›„, λ‹€λ₯Έ 경둜둜 μ΄λ™ν•˜μ—¬ νƒμƒ‰ν•˜λŠ” μ•Œκ³ λ¦¬μ¦˜μž…λ‹ˆλ‹€.

    • μ‹œμž‘ λ…Έλ“œμ—μ„œ μ΅œλŒ€ν•œ κ°€κΉŒμš΄ λ…Έλ“œ(이웃 λ…Έλ“œ) λΆ€ν„° μš°μ„  νƒμƒ‰ν•©λ‹ˆλ‹€.

    • BFSλŠ” P2P 파일 λ„€νŠΈμ›Œν¬μ—μ„œ ν”Όμ–΄(peer) λ…Έλ“œλ₯Ό 탐색할 λ•Œ ν™œμš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. BFSλ₯Ό ν™œμš©ν•΄ κ°€μž₯ κ°€κΉŒμš΄ 이웃 λ…Έλ“œλ§Œ λΉ λ₯΄κ²Œ 탐색할 수 μžˆμŠ΅λ‹ˆλ‹€.

  • 큐λ₯Ό μ΄μš©ν•˜μ—¬ κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • BFS κ΅¬ν˜„ μ˜ˆμ‹œ

    class Graph {
      constructor() {
        this.adjacencyList = {};
      }
      // 정점(λ…Έλ“œ) μΆ”κ°€
      addVertex(vertex) {
        if (!this.adjacencyList[vertex]) this.adjacencyList[vertex] = [];
        return this.adjacencyList;
      }
      // κ°„μ„  μΆ”κ°€
      addEdge(vertex1, vertex2) {
        this.adjacencyList[vertex1].push(vertex2);
        this.adjacencyList[vertex2].push(vertex1);
        return this.adjacencyList;
      }
    
      BFS(start) {
        const result = [];
        const visited = {};
        let vertex; // ν˜„μž¬ λ…Έλ“œ
        const adjacencyList = this.adjacencyList;
        // 탐색 μ‹œμž‘ λ…Έλ“œλ₯Ό 큐에 λ„£κ³ , λ°©λ¬Έ 처리
        const queue = [start];
        visited[start] = true;
        // 큐가 λΉ„μ–΄μžˆμ„ λ•ŒκΉŒμ§€ 반볡
        while (queue.length) {
          // console.log(queue);
          vertex = queue.shift(); // 
          result.push(vertex);
          // 인접 리슀트λ₯Ό μˆœνšŒν•˜μ—¬ λ°©λ¬Έν•˜μ§€ μ•Šμ€ 인접 λ…Έλ“œ
          // 큐에 pushν•˜κ³ , λ°©λ¬Έ 처리
          adjacencyList[vertex].forEach((v) => {
            if (!visited[v]) {
              queue.push(v); 
              visited[v] = true;
            }
          });
        }
        return result;
      }
    }
    const graph = new Graph();
    graph.addVertex("A");
    graph.addVertex("B");
    graph.addVertex("C");
    graph.addVertex("D");
    graph.addVertex("E");
    graph.addVertex("F");
    
    graph.addEdge("A", "B");
    graph.addEdge("A", "C");
    graph.addEdge("B", "D");
    graph.addEdge("C", "E");
    graph.addEdge("D", "E");
    graph.addEdge("D", "F");
    graph.addEdge("E", "F");
    
    graph.BFS("A")  //   ["A", "B", "C", "D", "E", "F"]

    β€» Big-O

    • μ‹œκ°„ λ³΅μž‘λ„λŠ” 두 탐색 μ•Œκ³ λ¦¬μ¦˜ λͺ¨λ‘ O(N) (N: κ·Έλž˜ν”„ λ…Έλ“œμ˜ 개수) μž…λ‹ˆλ‹€.

    • 곡간 λ³΅μž‘λ„λŠ” DFS보닀 BFS μƒλŒ€μ μœΌλ‘œ ν½λ‹ˆλ‹€.(더 λ§Žμ€ λ©”λͺ¨λ¦¬ 차지)

πŸ’‘μ •λ¦¬

  • BFSλŠ” 탐색해야 ν•  κ·Έλž˜ν”„μ˜ κΉŠμ΄κ°€ λ…Έλ“œλ§ˆλ‹€ λ‹€λ₯΄κ±°λ‚˜ 단일 λŒ€λ‹΅μ΄ ν•„μš”ν•œ 경우(μ΅œλ‹¨ 경둜 κ΅¬ν•˜κΈ° λ“±)에 μœ λ¦¬ν•©λ‹ˆλ‹€.

  • κ·Έλž˜ν”„μ˜ λͺ¨λ“  λ…Έλ“œλ₯Ό 탐색해야 ν•œλ‹€λ©΄ DFSκ°€ 더 쒋은 방법일 수 μžˆμŠ΅λ‹ˆλ‹€.

Reference

Graph | AWS

Graphs: breadth-first search | freeCodeCamp

Graphs: depth-first search | brilliant

Algorithm Visualizer

DFS와 BFS의 차이

BFS둜 μ΅œλ‹¨κ±°λ¦¬ μ°ΎκΈ° | freeCodeCamp

Last updated