題目描述:
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
來獲取,這樣可以保證代碼的正確性。