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 node0
to both nodes1
and2
. - Second node is labeled as
1
. Connect node1
to node2
. - Third node is labeled as
2
. Connect node2
to node2
(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;
}
};
方案二:
简单有效的方案是先第一遍遍历,创建所有结点,然后再给结点之间连接。这个原理跟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];
}
};