133. Clone Graph

133. Clone Graph

문제

Given a reference of a node in a connected undirected graph.

Return a deep copy (clone) of the graph.

Each node in the graph contains a value (int) and a list (List[Node]) of its neighbors.

class Node { public int val; public List neighbors; }

Test case format:

For simplicity, each node’s value is the same as the node’s index (1-indexed). For example, the first node with val == 1, the second node with val == 2, and so on. The graph is represented in the test case using an adjacency list.

An adjacency list is a collection of unordered lists used to represent a finite graph. Each list describes the set of neighbors of a node in the graph.

The given node will always be the first node with val = 1. You must return the copy of the given node as a reference to the cloned graph.

문제 사이트

Example 1: 그림

Input: adjList = [[2,4],[1,3],[2,4],[1,3]] Output: [[2,4],[1,3],[2,4],[1,3]] Explanation: There are 4 nodes in the graph. 1st node (val = 1)’s neighbors are 2nd node (val = 2) and 4th node (val = 4). 2nd node (val = 2)’s neighbors are 1st node (val = 1) and 3rd node (val = 3). 3rd node (val = 3)’s neighbors are 2nd node (val = 2) and 4th node (val = 4). 4th node (val = 4)’s neighbors are 1st node (val = 1) and 3rd node (val = 3).

Example 2: 그림

Input: adjList = [[]] Output: [[]] Explanation: Note that the input contains one empty list. The graph consists of only one node with val = 1 and it does not have any neighbors.

Example 3:

Input: adjList = [] Output: [] Explanation: This an empty graph, it does not have any nodes.

Constraints:

  • The number of nodes in the graph is in the range [0, 100].
  • 1 <= Node.val <= 100
  • Node.val is unique for each node.
  • There are no repeated edges and no self-loops in the graph.
  • The Graph is connected and all nodes can be visited starting from the given node.

문제 해석

  • 그래프 복사 문제, Node 출발 고정
  • 노드와 이웃 노드들은 연결리스트로 구현되어 있음
  • 클론을 만들고 원본의 neighbors까지 복사해야함
  • DFS-재귀로 인접한 노드들을 전부 순회한 후 백트래킹으로 노드와 이웃 노드들을 연결하는 알고리즘 사용

구현(DFS-재귀)

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> neighbors;
    Node() {
        val = 0;
        neighbors = vector<Node*>();
    }
    Node(int _val) {
        val = _val;
        neighbors = vector<Node*>();
    }
    Node(int _val, vector<Node*> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
};
*/

class Solution {
public:
    Node* DFS(Node* cur, unordered_map<Node*, Node*>& mp) {
        Node* clone = new Node(cur->val); // 복제 노드 생성
        mp[cur] = clone; // 중복 방지를 위한 hash table 저장
        vector<Node*> neighbor;

        for(auto it : cur->neighbors)
        {
            if(mp.find(it) != mp.end())
            {
                neighbor.push_back(mp[it]);
            }
            else
            {
                neighbor.push_back(DFS(it, mp));
            }
        }
        clone->neighbors = neighbor;
        return clone;
    }
    Node* cloneGraph(Node* node) {
        unordered_map<Node*, Node*> mp;
        if(!node)    return NULL;
        if(node->neighbors.size() == 0)
        {
            Node* clone = new Node(node->val);
            return clone;
        }
        return DFS(node, mp);
    }
};

코드 해설

  • hash table, backtracking 을 알아야 풀 수 완벽히 풀 수 있는 문제
  • 일단 이 문제는 읽고 Graph 관련 문제라는 것은 파악하기 쉬웠지만 구현하는데 어려움을 겪었다.
Node* clone = new Node(cur->val); // 복제 노드 생성
mp[cur] = clone; // 중복 방지를 위한 hash table 저장
  • 원본 노드가 들어오면 똑같이 복제한다.
  • 그리고 여기서 hash table 이 사용되는데 중복 복제를 막기위해 map을 사용하는 것이다.
  • cur = key, clone = value
for(auto it : cur->neighbors)
  • 반복문을 통해 cur의 이웃들을 확인하는 코드를 짠다.
if(mp.find(it) != mp.end())
{
    neighbor.push_back(mp[it]);
}
else
{
    neighbor.push_back(DFS(it, mp));
}
  • 만약 이웃이 이미 복제한 이웃이라면 if문이 실행될 것이고 새로운 이웃이라면 else문이 실행될 것이다.
  • Example 1를 예시로 들면 가장 먼저 DFS(1,mp)가 실행될 것이다.
    • 원본 1노드를 복제하고 map에 저장시킨 뒤 이웃 {2, 4}은 처음 만난 이웃이기 때문에 else문이 실행되며 재귀로 DFS(2,mp)가 실행될 것이다.
  • 재귀에 의해 DFS(2,mp) 실행된다.
    • 원본 2 노드를 복제하고 map에 저장시킨 뒤 이웃{1, 3} 중 1은 이미 복제한 이웃이기 때문에 if문이 실행되고 새로운 이웃 3노드에 의해 DFS(3,mp)가 실행된다.
  • 재귀에 의해 DFS(3,mp) 실행된다.
    • … (이하 동일) 이웃 {2, 4} 중 2은 이미 복제한 이웃이기 때문에 if문이 실행되고 새로운 이웃 4 노드에 의해 DFS(4,mp)가 실행된다.
  • 재귀에 의해 DFS(4,mp) 실행된다.
    • …(이하 동일) 이웃 {1, 3} 모두 이미 복제된 이웃들이다. 그렇기 때문에 vector neighbor = {1, 3} 이 들어가게 되고 clone->neighbors = neighbor 코드에 의해 이웃이 연결된다.
clone->neighbors = neighbor;
return clone;
  • 이 코드로 실행되었던 DFS(1,mp), DFS(2,mp), DFS(3,mp) 재귀가 back tracking(역추적) 에 의해 노드와 이웃 노드들이 연결되게 된다.
  • 정리하자면 이 문제의 핵심은 두가지라고 볼 수 있다.
    1. Hash table - map을 이용한 중복처리
    2. back tracking(역추적)을 이용한 이웃 연결