面试编码-将指向节点结构的指针作为参数,并返回传入数据结构的完整副本

Interview Coding - Take a pointer to a Node structure as a parameter and return a complete copy of the passed-in data structure

本文关键字:返回 副本 数据结构 参数 编码 节点 面试 指针 结构      更新时间:2023-10-16

这是一个我觉得很有趣的面试问题。

编写一个方法,该方法将指向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);
}