题目描述:
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 #.
First node is labeled as 0. Connect node 0 to both nodes 1 and 2.
Second node is labeled as 1. Connect node 1 to node 2. 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 / \ \_/
题目中给出的图的结构体如下:
struct UndirectedGraphNode {
int label;
vector<UndirectedGraphNode *> neighbors;
UndirectedGraphNode(int x) : label(x) {};
};
解法一(BFS):
广度优先算法(BFS)主要思路是首先看一个节点最多能够访问到哪些节点,再依次对这些节点进行访问和检索。BFS除了原本的图之外,还需要用到一个队列(queue)用于存储要访问的节点,以及一个标记节点是否已被访问的数组。
在本题中,除了要考虑节点是否已访问,还要考虑原节点和新创建的节点间的对应关系。在后边出现的节点q也有可能会和前边出现的节点p有边,p节点已经被访问过,应该有对应的新节点p_new,但p_new却不一定好找。也就是说,我们需要保存一张新旧节点对应关系的“地图”。所以需要使用map的结构。这里使用hash map来实现。
实现代码如下:
class Solution {
public:
UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
if(!node) return NULL;
unordered_map<UndirectedGraphNode*, UndirectedGraphNode*> nodeList;
UndirectedGraphNode *new_node = new UndirectedGraphNode(node->label);
nodeList[node] = new_node;
queue<UndirectedGraphNode *> tovisit;
tovisit.push(node);
while(!tovisit.empty()){
UndirectedGraphNode *cur_node = tovisit.front();
tovisit.pop();
for(UndirectedGraphNode* neigh : cur_node -> neighbors){
if(nodeList.find(neigh) == nodeList.end()){
UndirectedGraphNode *new_neigh = new UndirectedGraphNode(neigh->label);
nodeList[neigh] = new_neigh;
tovisit.push(neigh);
}
nodeList[cur_node]->neighbors.push_back(nodeList[neigh]);
}
}
return new_node;
}
};
解法二(DFS):
深度优先遍历(DFS)基于的想法很简单,就是探索迷宫需要的“线”和“粉笔”。线用来在探索迷宫走到尽头时返回出发点,而粉笔则用来标注已经访问过哪些点。类似地,DFS首先查看一个节点最远能够遍历到哪个节点,然后返回查看。这样,这个算法需要用到一个栈(stack)用于存储要访问的节点(与“线”的作用相同),以及一个标记节点是否已被访问的数组(与“粉笔”的作用相同)。
函数的递归调用是自动以栈的形式实现的,因此可以使用递归的方式来快速有效地完成DFS算法:
class Solution {
public:
UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
if(!node) return NULL;
UndirectedGraphNode *new_node = new UndirectedGraphNode(node->label);
nodeList[node] = new_node;
DFS(node);
return new_node;
}
void DFS(UndirectedGraphNode *cur_node){
for(UndirectedGraphNode* neigh : cur_node -> neighbors){
if(nodeList.find(neigh) == nodeList.end()){
UndirectedGraphNode *new_node = new UndirectedGraphNode(neigh->label);
nodeList[neigh] = new_node;
DFS(neigh);
}
nodeList[cur_node]->neighbors.push_back(nodeList[neigh]);
}
}
private:
unordered_map<UndirectedGraphNode *, UndirectedGraphNode *> nodeList;
};
Notes:
这里说一些在编程过程中出现的问题:
1、要注意考虑图为空时的情况。
2、要仔细考虑每一步用到的是原节点还是新节点,如果使用的是新节点的话,最好使用哈希表nodeList
来获取,这样可以保证代码的正确性。