【问题标题】:Linked list problems [duplicate]链表问题[重复]
【发布时间】:2010-08-11 21:45:04
【问题描述】:

可能重复:
Copy a linked list

你好堆栈溢出!我正在尝试了解有关链接列表的更多信息,因此我正在尝试创建一个可以深度复制链接列表的函数。我已经控制住了。困难的部分是输入列表将包含引用列表中其他随机节点的节点。

第一个问题是我不知道如何在列表中创建“随机”节点。截至目前,我只有“随机”节点等于“下一个”节点。

例如... pNext 引用下一个值,但 pReference 将引用列表中的随机节点。像 pReference 1 引用 3、2 引用 4、3 引用 1 和 4 引用 1。

第二个问题是我的代码正在处理“随机”节点值,但它依赖于原始副本。我希望它是一个深层副本,而不是依赖于原件。

#include <iostream>
#include <stdio.h>

using namespace std;

struct Node
{
    int Number; // An integer value.
    Node *pNext; // A pointer to the next node within the list.
    Node *pReference; // A pointer to a random node within the list.
};

void push(Node** head, int data, Node* reference)
{
    Node* newNode = new Node; 
    newNode->Number = data;
    newNode->pNext = *head;
    newNode->pReference = reference;
    *head = newNode;
}

Node* DuplicateLinkedList(Node *head)
{
    Node* current = head;
    Node* newHead = NULL;
    Node* newTail = NULL;

    while(current != NULL)
    {
        if(newHead == NULL)
        {
            push(&newHead, current->Number, (current->pReference));
            newTail = newHead;
        }
        else
        {
            push(&(newTail->pNext),current->Number, (current->pReference));
            newTail = newTail->pNext;
        }
        current = current->pNext;
    }

    return newHead;
}

int main()
{
    Node* headOfList= NULL;

    //Creating List for verification.
    for(int i=6; i>=1;i--)
    {
        push(&headOfList, i, headOfList);
    }

    //Call duplicate function.
    Node* copiedList = DuplicateLinkedList(headOfList);

    //Output for verification
    cout << endl << "Original: " << endl;
    while(headOfList != NULL)
    {
        cout << "Number: " << headOfList->Number << " ";
        cout << "pNext: " << headOfList->pNext << " ";
        cout << "pReference: " << headOfList->pReference << " " << endl;
        headOfList = headOfList->pNext;
    }
    cout << endl << endl;

    cout << endl << "Copied: " << endl;
    while(copiedList != NULL)
    {
        cout << "Number: " << copiedList->Number << " ";
        cout << "pNext: "  << copiedList->pNext << " ";
        cout << "pReference: " << copiedList->pReference << " " << endl;
        copiedList = copiedList->pNext;
    }
    cout << endl << endl;


    system("pause");
}

【问题讨论】:

  • “随机节点”是什么意思?
  • @David Thornley:这是一个比较常见的面试问题。随机节点是同一列表中的任意一个列表元素(节点)。
  • @Moron:是的。投票关闭。但这 10 分钟是一种有趣的分心工作。

标签: c++ linked-list


【解决方案1】:

使用 std::map 存储原始指针和新指针之间的转换。

遍历列表两次:一次创建新节点(pReference 设置为 NULL)并填充地图,第二次通过在地图中查找 pReference 成员来填充它们。

未经测试的代码:

Node* CopyNode(Node* src)
{
  if (src == NULL) return NULL;

  Node* newNode = new Node;
  newNode->number = src->number;
  newNode->pNext = NULL;
  newNode->pReference = NULL;

  return newNode;
}

Node* DeepCopy(Node* head)
{
  if (head == NULL) return NULL;

  std::map<Node*, Node*> mappings;

  Node* newHead = copyNode(head);
  mappings[head] = newHead;

  Node* newCurrent = newHead;
  for (Node* next = head->pNext; next != NULL; next = next->pNext)
  {
    Node* copy = CopyNode(next);
    mappings[next] = copy;

    newCurrent->pNext = copy;
    newCurrent = copy;
  }

  for (Node* current = head; current != NULL; current = current->pNext)
  {
    Node* newCurrent = mappings[current];
    newCurrent->pReference = mappings[current->pReference];
  }

  return newHead;
}

【讨论】:

    【解决方案2】:

    这是一个非常巧妙的算法,当你没有列表的大小时,我学会了在 O(N) 时间内从列表中获取随机元素。

    Node* GetRandomNode(Node* head)
    {
     int i = 1;
     srand ( time (NULL) );
     Node* temp = head;
     while ( head != NULL )
     { 
       if ( rand() % i == 0 ) temp = head;
       head = head->pNext;
       i++; 
     }
    
     return temp;
    }
    

    因此,您只需在每个节点的列表头部调用此函数即可获得均匀分布的随机节点。

    至于你的深拷贝问题,你只需要分配一个新节点并将你的节点的值复制到它里面。

    【讨论】:

    • 如果 rand() 永远不等于 i,则永远不会分配 Temp,导致您返回一个未初始化的变量。
    • 第一次执行循环体时,i == 0 所以总是使用 if()。但是你是对的,当 head 为 NULL 时 temp 未分配。
    • 感谢您指出这一点,我试图设置条件,以便第一个 rand() % 1 始终等于 0,但如果 head 为 NULL,它将返回一个未初始化的变量,所以它不是值得努力。关键是切换的 1/N 概率为您提供了均匀分布,当有人向我展示时我认为它很整洁。
    • -1 因为这根本不能回答问题。问题不是“我如何选择随机元素”。
    • @Sjoerd “第一个问题是我不知道如何在列表中创建‘随机’节点。”对不起,如果我误解了?
    【解决方案3】:

    如何简化。
    我通常先编写递归解决方案,然后将其转换为循环:

    Node* DuplicateLinkedList(Node* list)
    {
        std::map<Node*,Node*>   nodeMap;
        Node* result = DuplicateNode(nodeMap, list);
        resetRandomNodes(nodeMap, result);
        return result;
    }
    
    Node* DuplicateNode(std::map<Node*,Node*>& nodeMap, Node *node)
    {
        if (node== NULL)
        {    return NULL;
        }
    
        Node* result = new Node(node->Number, 
                                // Recursively copy the next element
                                DuplicateNode(nodeMap, node->pNext),
                                // For now store the original rand element
                                // We will fix this by iterating over the list again 
                                node->pReference
                               );
        // Keep a record of which node maps to which new node.
        nodeMap[node] = result;
    
        return result;
    }
    
    void resetRandomNodes(std::map<Node*,Node*>& nodeMap, Node *node)
    {
        if (node== NULL)
        {    return;
        }
    
        // Remember we stored the original random node (from the src list) in pReference.
        // Now we must replace this with the correct value. We can look this up in the
        // nodeMap we created when we copied the loop.
        node->pReference = nodeMap[node->pReference];
    
        // Recursively go through list.
        resetRandomNodes(nodeMap, node->pNext);
    }
    

    现在,为了提高效率,您只需将递归转换为循环。应该是比较琐碎的。完成后,您可以将三个函数合并为一个。

    【讨论】:

    • 如果你相信你的堆栈足够大,你甚至可以写 'new Node(node->Number, DuplicateNode(nodeMap, node->pNext), DuplicateNode(nodeMap, node->pReference) ',如果你在 DuplicateNode 的顶部添加一个简单的 if 来检查节点是否已经在 nodeMap 中。非常适合图形和其他非线性数据结构。
    • @Sjoerd:现在你不能那样做。然后,对于原始节点中的每个节点,您将创建两个副本并递归地复制它们。请记住,pReference 指向同一列表中的另一个节点。
    • 这就是为什么我会在 DuplicateNode 的顶部添加一个额外的 if 来检查节点是否已经在 nodeMap 中:'if (nodeMap[node] != NULL) return nodeMap[node];'
    • @Sjoerd:我明白了。我做的完全一样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-19
    • 1970-01-01
    • 2016-01-18
    相关资源
    最近更新 更多