LeetCode | Clone Graph(克隆图)

Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors.

OJ’s undirected graph serialization:

Nodes are labeled uniquely.

We use 
# as a separator for each node, and 
, as a separator for node label and each neighbor of the node.

As an example, consider the serialized graph {0,1,2#1,2#2,2}.

The graph has a total of three nodes, and therefore contains three parts as separated by #.

  1. First node is labeled as 0. Connect node 0 to both nodes 1 and 2.
  2. Second node is labeled as 1. Connect node 1 to node 2.
  3. Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a self-cycle.

Visually, the graph looks like the following:

       1
      / \
     /   \
    0 --- 2
         / \
         \_/

题目解析:

方案一:

由于链表之间有重复,所以我们要用hash函数来避免重复。利用map是个很好的选择,当一个值再其中时,也可以返回其指针。

我开始想着利用两个队列,两个map来处理,后来遇到了小问题,也没太多时间修改了,先把代码贴出来

//广度优先
class Solution {
public:
    UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
        if(node == NULL)
            return node;
        UndirectedGraphNode *clone = NULL;
        queue<UndirectedGraphNode *> originalqu;
        queue<UndirectedGraphNode *> clonequ;
        map<int,UndirectedGraphNode*> graphmap;
        map<int,UndirectedGraphNode*> orimap;
        originalqu.push(node);
        clone = new UndirectedGraphNode(node->label);
        clonequ.push(clone);
        graphmap[node->label] = clone;
        orimap[node->label] = node;
        while(!originalqu.empty()){
            UndirectedGraphNode *originalnode = originalqu.front();
            originalqu.pop();
            UndirectedGraphNode *clonenode = clonequ.front();
            clonequ.pop();

            for(int i = 0;i < originalnode->neighbors.size();i++){
                UndirectedGraphNode *tmp = (originalnode->neighbors)[i];

                map<int,UndirectedGraphNode*>::iterator it = graphmap.find(tmp->label);
                if(it != graphmap.end()){
                    clonenode->neighbors.push_back(it->second);
                    clonequ.push(it->second);
                }else{
                    UndirectedGraphNode *p = new UndirectedGraphNode(tmp->label);
                    (clonenode->neighbors).push_back(p);
                    graphmap[tmp->label] = p;
                    clonequ.push(p);
                }
                if(orimap.find(tmp->label) != orimap.end())
                    continue;
                orimap[tmp->label] = tmp;
                originalqu.push(tmp);
            }
        }
        return clone;
    }
};

《LeetCode | Clone Graph(克隆图)》



方案二:

简单有效的方案是先第一遍遍历,创建所有结点,然后再给结点之间连接。这个原理跟Copy List with Random Pointer差不多,先创建好以后,就方便给指针定位了。

class Solution {
public:
    UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
        if(node==NULL)
        {
            return NULL;
        }
        map<int, UndirectedGraphNode*> nodes;
        queue<UndirectedGraphNode*> q;

        q.push(node);

        while(!q.empty())
        {
            UndirectedGraphNode* tmp = q.front();
            q.pop();
            if(nodes.find(tmp->label)==nodes.end()) //避免重复
            {
                UndirectedGraphNode* new_node = new UndirectedGraphNode(tmp->label);
                nodes.insert(pair<int, UndirectedGraphNode*>(new_node->label, new_node));
                for(int i=0;i<tmp->neighbors.size();i++)
                {
                    q.push(tmp->neighbors[i]);  //先将子节点全部放入队列中,无论有没有重复,然后出栈创建元素时再判断
                }
            }
        }

        q.push(node);
        while(!q.empty())
        {
            UndirectedGraphNode* tmp = q.front();
            q.pop();
            UndirectedGraphNode* existingnode = nodes[tmp->label];
            if(existingnode->neighbors.empty()&&!tmp->neighbors.empty())
            {
                for(int i=0;i<tmp->neighbors.size();i++)
                {
                    existingnode->neighbors.push_back(nodes[tmp->neighbors[i]->label]);
                    q.push(tmp->neighbors[i]);
                }
            }
        }

        return nodes[node->label];
    }
};

方案三:

更方便的方案是创建两个指针的map函数,就将我上面的方案一统一起来,更容易查找。

思路

1. 这题明说, label 独一无二, 那么就可以使用 hash map 存储元素

2. BFS 搜索, 边搜边向 hash map 添加元素

3. 在设置标记位上 TLE 了 N 次, 一个元素一旦被假如到 hash map, 就说明该元素已经被访问到了并已被假如到 queue 中, 同时环的问题也被克服了. 我在做的时候, 把环的问题拉出来单独处理, 但标记忘记了

4. unordered_map<node*, node*> 这种设置不是第一次见到了, 比设置成 unordered_map<int, node*> 要方便一些

5. map.count 比 map.find 要精练一些

6. 加入 map 时, 可以直接 map[] = xxx, 不用判断是否已有

class Solution {
public:
    UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
        unordered_map<UndirectedGraphNode*, UndirectedGraphNode*> record;
        if(node == NULL)
            return node;

        deque<UndirectedGraphNode*> queue;
        queue.push_back(node);

        while(!queue.empty()) {
            UndirectedGraphNode *nextNode = queue.front();
            queue.pop_front();

            if(!record.count(nextNode)) {   //只有在第一个元素时创建该结点,后面的已经在for循环中创建好
                UndirectedGraphNode *newNode = new UndirectedGraphNode(nextNode->label);
                record[nextNode] = newNode;
            }
            for(int i = nextNode->neighbors.size()-1; i >= 0 ; i --) {
                UndirectedGraphNode *childNode = nextNode->neighbors[i];
                if(!record.count(childNode)) {  //如果没有在map中,就创建结点并放入队列中
                    UndirectedGraphNode *newNode = new UndirectedGraphNode(childNode->label);
                    record[childNode] = newNode;
                    queue.push_back(childNode);
                }   //通过map的键值找到克隆的指针,直接将要链接的子节点的克隆填入,方法太好了
                record[nextNode]->neighbors.push_back(record[childNode]);
            }
        }
        return record[node];
    }
};
点赞