面试编码-将指向节点结构的指针作为参数,并返回传入数据结构的完整副本
Interview Coding - Take a pointer to a Node structure as a parameter and return a complete copy of the passed-in data structure
这是一个我觉得很有趣的面试问题。
编写一个方法,该方法将指向Node结构的指针作为参数,并返回传入数据结构的完整副本。
Node结构包含指向其他Node结构的两个指针。例如,方法签名可能看起来是这样的:
Node* Copy(Node* root);
注意-不要对数据结构做出任何假设-它可以是树、链表、图表等。
如何对任何数据结构做到这一点
在通用图的情况下,您需要从原始图中的节点到新图中节点的映射,以便在遇到循环时创建正确的链接。如果你碰巧在每个节点中都有额外的临时空间,足够大,可以容纳一个指针,那么你可以直接将映射存储在节点中;否则,您将需要使用外部映射,例如关联数组或哈希表。
然后,只需要遍历图形、复制节点并查找相应的边。类似这样的东西:
struct Node
{
Node(int _data) : data(_data) { memset(links, 0, sizeof(links)); }
int data;
Node *links[2];
}
Node *Copy(Node *root)
{
typedef std::map<Node*, Node*> NodeMap;
NodeMap nodeMap;
std::deque<Node*> nodesToVisit;
// Set up initial new root and mapping for the root
Node *newRoot = new Node(root->data);
nodeMap[root] = newRoot;
// Breadth-first search the graph
nodesToVisit.push_back(root);
while(!nodesToVisit.empty())
{
Node *cur = nodesToVisit.front();
nodesToVisit.pop_front();
Node *newCur = nodeMap[cur];
for(int i = 0; i < 2; i++)
{
Node *link = cur->links[i];
if(link)
{
// If we've already created the corresponding node for this
// link, use that. Otherwise, create it and add it to the map.
NodeMap::iterator mappedLink = nodeMap.find(link);
if(mappedLink != nodeMap.end())
{
newCur->links[i] = mappedLink->second;
}
else
{
Node *newLink = new Node(link->data);
nodeMap[link] = newLink;
newCur->links[i] = newLink;
nodesToVisit.push_back(link);
}
}
}
}
return newRoot;
}
上述问题是不可能的。您必须假设整个数据结构完全存储在可从初始节点访问的节点的内容中。但这不是一个你可以做的假设。即使是标准的基本双链接列表也可能不符合该描述。
class Copier {
std::map <Node*, Node*> copies;
Node* Copy(Node* n) {
if (!n) return 0;
Node*& copy = copies[n];
if (!copy) {
copy = new Node();
copy.node1 = Copy(n.node1);
copy.node2 = Copy(n.node2);
}
return copy;
}
}
Node* Copy(Node* root) {
if (root == NULL)
return root;
std::unordered_map<Node*, Node*> completed;
std::deque<Node*> todo;
Node *ret = new Node(*scur);
completed.push_back(std::make_pair(root, ret));
todo.push_pack(root);
//while there's more nodes to duplicate
do {
//duplicate the node
Node* oldNode = todo.back();
Node* newNode = completed[cur];
todo.pop_back();
if(oldNode->left) {
auto iter = completed.find(oldNode->left);
//if it has a left child that needs duplicating, add it to the todo list
if (iter == completed.end()) {
newNode->left = new Node(*(oldNode->left));
completed.push_back(std::make_pair(oldNode->left, newNode->left));
todo.push_back(oldNode->left);
} else {
newNode->left = completed[oldNode->left];
}
}
if(oldNode->right) {
auto iter = completed.find(oldNode->right);
//if it has a right child that needs duplicating, add it to the todo list
if (iter == completed.end()) {
newNode->right = new Node(*(oldNode->right));
completed.push_back(std::make_pair(oldNode->right, newNode->right));
todo.push_back(oldNode->right);
} else {
newNode->right= completed[oldNode->right];
}
}
} while(todo.empty() == false)
//return the translation of the root
return ret;
}
没有堆栈溢出,根可以为NULL,如果左侧或右侧为NULL,则不会失败。
[编辑]Adam Rosenfield让我意识到,如果网络中存在环路,这是不正确的。不得不几乎从头开始重写。由于需要大量的代码,我更喜欢他的循环代码。
return new Node(*node);
恶作剧问题?
您应该递归地编写它;
Node * Copy( Node * root )
{
Node * node_copy;
node_copy = new Node; // Assume Node1 and Node2 are initialized to 0
node_copy->content = root->content;
if( root->Node1 ) node_copy->Node1 = Copy( root->Node1 );
if( root->Node2 ) node_copy->Node2 = Copy( root->Node2 );
return node_copy;
}
因此,这并没有对数据类型做出任何假设
假设存在一个只复制节点内容而不复制其子节点的复制构造函数:
Node* Copy(Node* root)
{
Node* copy = new Node(*root);
copy->left = Copy(root->left);
copy->right = Copy(root->right);
return copy;
}
在更一般的意义上,我会使用完全复制整个数据结构的复制构造函数:
Node* Copy(Node* root)
{
return new Node(*root);
}
相关文章:
- 使用unique_ptr并返回引用,或者我应该使用shared_ptr并在需要时制作副本
- C 标准:通过复制返回以初始化无RVO的参考:是否有任何副本
- 如何在模板类中返回成员变量的副本
- 如何声明接受转发引用并返回引用或副本的函数模板
- 有条件的操作员从其参数的副本中返回值
- 我可以返回副本作为参考吗?
- 返回不带副本 ctor 的引用
- 'std::any_cast' 返回一个副本
- 按值副本返回,而不是移动
- 参考参数返回副本
- C++方法返回自身副本时内存泄漏
- 为什么迭代器运算符 + 返回副本
- 节点.js C++ 插件:返回新副本
- 如何通过制作副本从函数返回引用
- 为什么返回 std::vector 仍然是在制作副本
- 就地修改并返回副本有意义吗
- 为什么在 constexpr 函数中允许返回对常量指针的引用,但不返回副本
- 按方法返回副本或引用
- 在c++中通过成员函数返回引用比返回副本快
- 由于继承,堆栈返回副本