使用此版本的代码,我在使用 copy-> neighbors.push_back 时收到错误“ AddressSanitizer ..新操作符中的stackoverflow(无符号长)” em>
class Node {
public:
int val;
vector<Node*> neighbors;
Node() {}
Node(int _val, vector<Node*> _neighbors) {
val = _val;
neighbors = _neighbors;
}
};
unordered_map<Node*, Node*> copies;
Node* cloneGraph(Node* node) {
if(!node) return node;
if(copies.find(node)==copies.end()){
Node *copy = new Node(node->val,{});
for(auto neighbor:node->neighbors){
copy->neighbors.push_back(cloneGraph(neighbor));//stackoverflow
}
copies[node]= copy;
}
return copies[node];
}
但是它与我使用 copies [node]-> neighbors.push_back 的该版本一起使用,为什么会发生这种情况?
唯一的区别是使用对全局映射元素的引用: copies [node] 与本地指针 copy
Node* cloneGraph(Node* node) {
if(!node) return node;
if(copies.find(node)==copies.end()){
copies[node] = new Node(node->val,{});
for(auto neighbor:node->neighbors){
copies[node]->neighbors.push_back(cloneGraph(neighbor));
}
}
return copies[node];
}
答案 0 :(得分:2)
在第一个实现中,您将在每个递归调用中创建一个新的Node,并将其推送到堆栈中。而在第二种实现中,它被放置在一个数组中,该数组不是局部递归变量的一部分(它看起来像全局变量),因此堆栈不需要跟踪新创建的节点。
答案 1 :(得分:2)
当递归函数导致堆栈溢出时,您首先要寻找的就是无限递归。
考虑一个包含两个节点的简单图:A
是B
的邻居,而B
是A
的邻居(非定向图的相当标准) 。致电cloneGraph(&A)
会怎样?
A
不在地图上,因此进行了克隆。cloneGraph(&B)
。那接下来会发生什么?
B
不在地图上,因此进行了克隆。cloneGraph(&A)
。好的,回到我们开始的地方。如果递归继续下去,这可能会很难看。所以最大的问题是
A
在地图上吗? 使用代码的第一个版本,不是。因此重复递归直到堆栈溢出。使用第二版代码,因此递归到此为止。
答案 2 :(得分:1)
在第一个版本中,您可以使用递归为具有周期的图生成无限循环。请注意,进入更深层次的递归的条件是,在映射copies
中找不到节点,但是仅在整个递归完成后才更新此映射。
如果图形是A-> B和B-> A,则对cloneGraph(&A)
的调用将调用cloneGraph(&B)
,这将无限期地调用cloneGraph(&A)
,依此类推,直到调用堆栈没有空间了。
答案 3 :(得分:1)
请仔细考虑您的算法。大概您的图形具有圆柱。
由于您的第一个版本仅在将新创建的节点递归到copies
之后才将其添加到cloneGraph
,所以下一个调用将尝试再次克隆相同的节点,这将递归,等等。